From 95cffe22fe7ab68deb9f5dd8f056e865b9ec4825 Mon Sep 17 00:00:00 2001 From: paulc Date: Fri, 22 Oct 2004 02:45:19 +0000 Subject: [PATCH] Lots of stability improvments and bug fixes. git-svn-id: http://voip.null.ro/svn/yate@91 acf43c95-373e-0410-b603-e72c3f656dc1 --- engine/Message.cpp | 3 +- engine/Thread.cpp | 6 ++-- modules/extmodule.cpp | 2 +- modules/h323chan.cpp | 57 ++++++++++++++++++------------ modules/rmanager.cpp | 8 ++--- modules/tonegen.cpp | 4 +-- modules/wavefile.cpp | 16 ++++----- modules/zapchan.cpp | 80 ++++++++++++++++++++++++++----------------- yate.8 | 3 ++ yatengine.h | 2 +- 10 files changed, 107 insertions(+), 74 deletions(-) diff --git a/engine/Message.cpp b/engine/Message.cpp index e95e9f38..123c5477 100644 --- a/engine/Message.cpp +++ b/engine/Message.cpp @@ -242,11 +242,10 @@ bool MessageDispatcher::dispatch(Message &msg) bool MessageDispatcher::enqueue(Message *msg) { + Lock lock(m_mutex); if (!msg || m_messages.find(msg)) return false; - m_mutex.lock(); m_messages.append(msg); - m_mutex.unlock(); return true; } diff --git a/engine/Thread.cpp b/engine/Thread.cpp index 0865e1a7..ab0b0b55 100644 --- a/engine/Thread.cpp +++ b/engine/Thread.cpp @@ -204,7 +204,7 @@ void ThreadPrivate::killall() } ::usleep(10); if (++c >= 10) { - Debug(DebugFail,"Could not kill %p, will use sledgehammer later.",t); + Debug(DebugGoOn,"Could not kill %p, will use sledgehammer later.",t); sledgehammer = true; t->m_thread = 0; l = l->next(); @@ -217,10 +217,10 @@ void ThreadPrivate::killall() // usually too big since many libraries have threads of their own... if (sledgehammer) { #ifdef __linux__ - Debug(DebugFail,"Brutally killing remaining threads!"); + Debug(DebugGoOn,"Brutally killing remaining threads!"); ::pthread_kill_other_threads_np(); #else - Debug(DebugFail,"Aargh! I cannot kill remaining threads on this platform!"); + Debug(DebugGoOn,"Aargh! I cannot kill remaining threads on this platform!"); #endif } } diff --git a/modules/extmodule.cpp b/modules/extmodule.cpp index 024119d3..de3501e6 100644 --- a/modules/extmodule.cpp +++ b/modules/extmodule.cpp @@ -740,7 +740,7 @@ bool ExtModHandler::received(Message &msg) else if (t == "playrec") typ = ExtModChan::DataBoth; else { - Debug(DebugFail,"Invalid ExtModule method '%s', use 'nochan', 'nodata', 'play', 'record' or 'playrec'", + Debug(DebugGoOn,"Invalid ExtModule method '%s', use 'nochan', 'nodata', 'play', 'record' or 'playrec'", t.c_str()); return false; } diff --git a/modules/h323chan.cpp b/modules/h323chan.cpp index aad612de..b8c24914 100644 --- a/modules/h323chan.cpp +++ b/modules/h323chan.cpp @@ -257,12 +257,13 @@ public: class H323MsgThread : public Thread { public: - H323MsgThread(Message *msg, YateH323Connection *conn) - : Thread("H323MsgThread"), m_msg(msg), m_conn(conn) { } + H323MsgThread(Message *msg, const char *id) + : Thread("H323MsgThread"), m_msg(msg), m_id(id) { } virtual void run(); + virtual void cleanup(); private: Message *m_msg; - YateH323Connection *m_conn; + String m_id; }; class StatusHandler : public MessageHandler @@ -296,29 +297,41 @@ static H323Plugin hplugin; void H323MsgThread::run() { + ::usleep(1000); Engine::dispatch(m_msg); *m_msg = "preroute"; Engine::dispatch(m_msg); *m_msg = "route"; - if (Engine::dispatch(m_msg) && !m_msg->retValue().null()) { + bool ok = Engine::dispatch(m_msg) && !m_msg->retValue().null(); + YateH323Connection *conn = hplugin.findConnectionLock(m_id); + if (!conn) { + Debug(DebugMild,"YateH323Connection '%s' wanished while routing!",m_id.c_str()); + return; + } + if (ok) { + conn->AnsweringCall(H323Connection::AnswerCallPending); *m_msg = "call"; m_msg->addParam("callto",m_msg->retValue()); m_msg->retValue() = 0; - m_msg->userData(static_cast(m_conn)); + m_msg->userData(static_cast(conn)); if (Engine::dispatch(m_msg)) { - Debug(DebugInfo,"Routing H.323 [%p] call to '%s'",m_conn,m_msg->getValue("callto")); - m_conn->deref(); - m_conn->AnsweringCall(H323Connection::AnswerCallNow); + Debug(DebugInfo,"Routing H.323 [%p] call to '%s'",conn,m_msg->getValue("callto")); + conn->AnsweringCall(H323Connection::AnswerCallNow); + conn->deref(); } else { - Debug(DebugInfo,"Rejecting unconnected H.323 [%p] call",m_conn); - m_conn->AnsweringCall(H323Connection::AnswerCallDenied); + conn->AnsweringCall(H323Connection::AnswerCallDenied); } } else { - Debug(DebugInfo,"Rejecting unrouted H.323 [%p] call",m_conn); - m_conn->AnsweringCall(H323Connection::AnswerCallDenied); + Debug(DebugInfo,"Rejecting unrouted H.323 [%p] call",conn); + conn->AnsweringCall(H323Connection::AnswerCallDenied); } + conn->Unlock(); +} + +void H323MsgThread::cleanup() +{ delete m_msg; } @@ -338,7 +351,7 @@ BOOL YateGatekeeperServer::Init () int i; for (i = 1; (addr = s_cfg.getValue("gk",("interface"+String(i)).c_str())); i++){ if (!AddListener(new H323GatekeeperListener(endpoint, *this,s_cfg.getValue("gk","name","YateGatekeeper"),new H323TransportUDP(endpoint,PIPSocket::Address(addr),s_cfg.getIntValue("gk","port",1719),0)))) - Debug(DebugFail,"I can't start the listener for address: %s",addr); + Debug(DebugGoOn,"I can't start the listener for address: %s",addr); } Debug(DebugInfo,"i = %d",i); return TRUE; @@ -416,7 +429,7 @@ bool YateH323EndPoint::Init(void) if (s_cfg.getBoolValue("ep","ep",true)) { H323ListenerTCP *listener = new H323ListenerTCP(*this,addr,port); if (!(listener && StartListener(listener))) { - Debug(DebugFail,"Unable to start H323 Listener at port %d",port); + Debug(DebugGoOn,"Unable to start H323 Listener at port %d",port); if (listener) delete listener; return false; @@ -437,7 +450,7 @@ bool YateH323EndPoint::Init(void) if (SetGatekeeper(gkName, rasChannel)) Debug(DebugInfo,"Connect to gatekeeper ip = %s",d); else { - Debug(DebugFail,"Unable to connect to gatekeeper ip = %s",d); + Debug(DebugGoOn,"Unable to connect to gatekeeper ip = %s",d); if (listener) listener->Close(); } @@ -446,7 +459,7 @@ bool YateH323EndPoint::Init(void) if (LocateGatekeeper(gkIdentifier)) Debug(DebugInfo,"Connect to gatekeeper name = %s",a); else { - Debug(DebugFail,"Unable to connect to gatekeeper name = %s",a); + Debug(DebugGoOn,"Unable to connect to gatekeeper name = %s",a); if (listener) listener->Close(); } @@ -454,7 +467,7 @@ bool YateH323EndPoint::Init(void) if (DiscoverGatekeeper(new H323TransportUDP(*this))) Debug(DebugInfo,"Find a gatekeeper"); else { - Debug(DebugFail,"Unable to connect to any gatekeeper"); + Debug(DebugGoOn,"Unable to connect to any gatekeeper"); if (listener) listener->Close(); return false; @@ -540,8 +553,8 @@ H323Connection::AnswerCallResponse YateH323Connection::OnAnswerCall(const PStrin if (s) m->addParam("calledname",s); #endif - new H323MsgThread(m,this); - return H323Connection::AnswerCallPending; + H323MsgThread *t = new H323MsgThread(m,id()); + return t->error() ? H323Connection::AnswerCallDenied : H323Connection::AnswerCallDeferred; } void YateH323Connection::OnEstablished() @@ -809,6 +822,8 @@ BOOL YateH323AudioConsumer::IsOpen() const void YateH323AudioConsumer::Consume(const DataBlock &data, unsigned long timeDelta) { + if (m_exit) + return; Lock lock(m_mutex); if ((m_buffer.length() + data.length()) <= (480*5)) m_buffer += data; @@ -821,7 +836,7 @@ void YateH323AudioConsumer::Consume(const DataBlock &data, unsigned long timeDel BOOL YateH323AudioConsumer::Read(void *buf, PINDEX len) { - for (;;) { + while (!m_exit) { Lock lock(m_mutex); if (len >= (int)m_buffer.length()) { ref(); @@ -1052,7 +1067,7 @@ bool H323Handler::received(Message &msg) if (!dest.matches(r)) return false; if (!msg.userData()) { - Debug(DebugFail,"H.323 call found but no data channel!"); + Debug(DebugWarn,"H.323 call found but no data channel!"); return false; } Debug(DebugInfo,"Found call to H.323 target='%s'", diff --git a/modules/rmanager.cpp b/modules/rmanager.cpp index f092a1e9..7735547f 100644 --- a/modules/rmanager.cpp +++ b/modules/rmanager.cpp @@ -139,7 +139,7 @@ Connection::~Connection() void Connection::run() { if (::fcntl(m_socket,F_SETFL,O_NONBLOCK)) { - Debug("RManager",DebugFail, "Failed to set tcp socket to nonblocking mode: %s\n", strerror(errno)); + Debug("RManager",DebugGoOn, "Failed to set tcp socket to nonblocking mode: %s\n", strerror(errno)); return; } // For the sake of responsiveness try to turn off the tcp assembly timer @@ -426,19 +426,19 @@ void RManager::initialize() bindaddr.sin_addr.s_addr = inet_addr(host); bindaddr.sin_port = htons(port); if (sock < 0) { - Debug("RManager",DebugFail,"Unable to create the listening socket: %s",strerror(errno)); + Debug("RManager",DebugGoOn,"Unable to create the listening socket: %s",strerror(errno)); return; } const int reuseFlag = 1; ::setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,(const char*)&reuseFlag,sizeof reuseFlag); if (::bind(sock, (struct sockaddr *)&bindaddr, sizeof(bindaddr)) < 0) { - Debug("RManager",DebugFail,"Failed to bind to %s:%u : %s",inet_ntoa(bindaddr.sin_addr),ntohs(bindaddr.sin_port),strerror(errno)); + Debug("RManager",DebugGoOn,"Failed to bind to %s:%u : %s",inet_ntoa(bindaddr.sin_addr),ntohs(bindaddr.sin_port),strerror(errno)); ::close(sock); sock = -1; return; } if (listen(sock, 2)) { - Debug("RManager",DebugFail,"Unable to listen on socket: %s\n", strerror(errno)); + Debug("RManager",DebugGoOn,"Unable to listen on socket: %s\n", strerror(errno)); ::close(sock); sock = -1; return; diff --git a/modules/tonegen.cpp b/modules/tonegen.cpp index 6ff3fa4f..e71e1032 100644 --- a/modules/tonegen.cpp +++ b/modules/tonegen.cpp @@ -249,7 +249,7 @@ bool ToneHandler::received(Message &msg) m.userData(tc); if (Engine::dispatch(m)) return true; - Debug(DebugFail,"Tone outgoing call not accepted!"); + Debug(DebugWarn,"Tone outgoing call not accepted!"); delete tc; } else @@ -280,7 +280,7 @@ bool AttachHandler::received(Message &msg) Debug(DebugWarn,"No source tone '%s' could be attached to [%p]",src.c_str(),dd); } else - Debug(DebugFail,"Tone '%s' attach request with no data channel!",src.c_str()); + Debug(DebugWarn,"Tone '%s' attach request with no data channel!",src.c_str()); return false; } diff --git a/modules/wavefile.cpp b/modules/wavefile.cpp index 010fe584..061610bb 100644 --- a/modules/wavefile.cpp +++ b/modules/wavefile.cpp @@ -105,7 +105,7 @@ WaveSource::WaveSource(const String& file, DataEndpoint *chan, bool autoclose) if (m_fd >= 0) start("WaveSource"); else - Debug(DebugFail,"Opening '%s': error %d: %s", + Debug(DebugGoOn,"Opening '%s': error %d: %s", file.c_str(), errno, ::strerror(errno)); } @@ -183,7 +183,7 @@ WaveConsumer::WaveConsumer(const String& file, DataEndpoint *chan, unsigned maxl m_format = "mulaw"; m_fd = ::creat(file.safe(),S_IRUSR|S_IWUSR); if (m_fd < 0) - Debug(DebugFail,"Creating '%s': error %d: %s", + Debug(DebugGoOn,"Creating '%s': error %d: %s", file.c_str(), errno, ::strerror(errno)); } @@ -272,7 +272,7 @@ bool WaveHandler::received(Message &msg) if (dest.matchString(1) == "record") meth = true; else if (dest.matchString(1) != "play") { - Debug(DebugFail,"Invalid wavefile method '%s', use 'record' or 'play'", + Debug(DebugWarn,"Invalid wavefile method '%s', use 'record' or 'play'", dest.matchString(1).c_str()); return false; } @@ -306,7 +306,7 @@ bool WaveHandler::received(Message &msg) m.userData(c); if (Engine::dispatch(m)) return true; - Debug(DebugFail,"Wave outgoing call not accepted!"); + Debug(DebugWarn,"Wave outgoing call not accepted!"); delete c; } else @@ -328,7 +328,7 @@ bool AttachHandler::received(Message &msg) more--; } else { - Debug(DebugFail,"Could not attach source with method '%s', use 'play'", + Debug(DebugWarn,"Could not attach source with method '%s', use 'play'", src.matchString(1).c_str()); src = ""; } @@ -348,7 +348,7 @@ bool AttachHandler::received(Message &msg) more--; } else { - Debug(DebugFail,"Could not attach consumer with method '%s', use 'record'", + Debug(DebugWarn,"Could not attach consumer with method '%s', use 'record'", cons.matchString(1).c_str()); cons = ""; } @@ -364,9 +364,9 @@ bool AttachHandler::received(Message &msg) DataEndpoint *dd = static_cast(msg.userData()); if (!dd) { if (!src.null()) - Debug(DebugFail,"Wave source '%s' attach request with no data channel!",src.c_str()); + Debug(DebugWarn,"Wave source '%s' attach request with no data channel!",src.c_str()); if (!cons.null()) - Debug(DebugFail,"Wave consumer '%s' attach request with no data channel!",cons.c_str()); + Debug(DebugWarn,"Wave consumer '%s' attach request with no data channel!",cons.c_str()); return false; } diff --git a/modules/zapchan.cpp b/modules/zapchan.cpp index c225e83f..f20e08a1 100644 --- a/modules/zapchan.cpp +++ b/modules/zapchan.cpp @@ -72,19 +72,19 @@ static int zt_open(int channo, bool subchan,unsigned int blksize) Debug("ZapChan",DebugInfo,"Open zap channel=%d with block size=%d",channo,blksize); int fd = ::open(subchan ? "/dev/zap/pseudo" : "/dev/zap/channel",O_RDWR|O_NONBLOCK); if (fd < 0) { - Debug("ZapChan",DebugFail,"Failed to open zap device: error %d: %s",errno,::strerror(errno)); + Debug("ZapChan",DebugGoOn,"Failed to open zap device: error %d: %s",errno,::strerror(errno)); return -1; } if (channo) { if (::ioctl(fd, subchan ? ZT_CHANNO : ZT_SPECIFY, &channo)) { - Debug("ZapChan",DebugFail,"Failed to specify chan %d: error %d: %s",channo,errno,::strerror(errno)); + Debug("ZapChan",DebugGoOn,"Failed to specify chan %d: error %d: %s",channo,errno,::strerror(errno)); ::close(fd); return -1; } } if (blksize) { if (::ioctl(fd, ZT_SET_BLOCKSIZE, &blksize) == -1) { - Debug("ZapChan",DebugFail,"Failed to set block size %d: error %d: %s",blksize,errno,::strerror(errno)); + Debug("ZapChan",DebugGoOn,"Failed to set block size %d: error %d: %s",blksize,errno,::strerror(errno)); ::close(fd); return -1; } @@ -102,7 +102,9 @@ static bool zt_set_law(int fd, int law) if (::ioctl(fd, ZT_SETLAW, &law) != -1) return true; +#ifdef DEBUG Debug("ZapChan",DebugInfo,"Failed to set law %d: error %d: %s",law,errno,::strerror(errno)); +#endif return false; } @@ -380,6 +382,7 @@ public: void idle(); void restart(); bool open(int defLaw = -1); + void close(); inline void setTimeout(unsigned long long tout) { m_timeout = tout ? Time::now()+tout : 0; } const char *status() const; @@ -404,7 +407,7 @@ class ZapSource : public ThreadedSource { public: ZapSource(ZapChan *owner,unsigned int bufsize) - : m_owner(owner), m_bufsize(bufsize) + : m_owner(owner), m_bufsize(bufsize), m_buf(0,bufsize) { Debug(DebugAll,"ZapSource::ZapSource(%p) [%p]",owner,this); start("ZapSource"); @@ -418,6 +421,8 @@ public: private: ZapChan *m_owner; unsigned int m_bufsize; + DataBlock m_buf; + DataBlock m_data; }; class ZapConsumer : public DataConsumer @@ -499,18 +504,18 @@ struct pri *PriSpan::makePri(int fd, int dchan, int nettype, int swtype, int nsf if (dchan >= 0) { // Set up the D channel if we have one if (::ioctl(fd,ZT_SPECIFY,&dchan) == -1) { - Debug("PriSpan",DebugFail,"Failed to open D-channel %d: error %d: %s", + Debug("PriSpan",DebugGoOn,"Failed to open D-channel %d: error %d: %s", dchan,errno,::strerror(errno)); return 0; } ZT_PARAMS par; if (::ioctl(fd, ZT_GET_PARAMS, &par) == -1) { - Debug("PriSpan",DebugFail,"Failed to get parameters of D-channel %d: error %d: %s", + Debug("PriSpan",DebugWarn,"Failed to get parameters of D-channel %d: error %d: %s", dchan,errno,::strerror(errno)); return 0; } if (par.sigtype != ZT_SIG_HDLCFCS) { - Debug("PriSpan",DebugFail,"D-channel %d is not in HDLC/FCS mode",dchan); + Debug("PriSpan",DebugWarn,"D-channel %d is not in HDLC/FCS mode",dchan); return 0; } ZT_BUFFERINFO bi; @@ -519,7 +524,7 @@ struct pri *PriSpan::makePri(int fd, int dchan, int nettype, int swtype, int nsf bi.numbufs = 16; bi.bufsize = 1024; if (::ioctl(fd, ZT_SET_BUFINFO, &bi) == -1) { - Debug("PriSpan",DebugFail,"Could not set buffering on D-channel %d",dchan); + Debug("PriSpan",DebugWarn,"Could not set buffering on D-channel %d",dchan); return 0; } } @@ -557,7 +562,7 @@ PriSpan::~PriSpan() c->destruct(); } } - delete m_chans; + delete[] m_chans; ::close(m_fd); } @@ -584,7 +589,7 @@ void PriSpan::run() else if (sel > 0) ev = ::pri_check_event(m_pri); else if (errno != EINTR) - Debug("PriSpan",DebugFail,"select() error %d: %s", + Debug("PriSpan",DebugGoOn,"select() error %d: %s", errno,::strerror(errno)); if (ev) { if (dumpEvents && debugAt(DebugAll)) @@ -826,29 +831,28 @@ void PriSpan::proceedingChan(int chan) void ZapSource::run() { - DataBlock buf(0,m_bufsize); - DataBlock data; + int rd = 0; for (;;) { Thread::yield(); int fd = m_owner->fd(); if (fd != -1) { - int rd = ::read(fd,buf.data(),buf.length()); + rd = ::read(fd,m_buf.data(),m_buf.length()); #ifdef DEBUG Debug(DebugAll,"ZapSource read %d bytes",rd); #endif if (rd > 0) { switch (m_owner->law()) { case -1: - data.assign(buf.data(),rd); - Forward(data,rd/2); + m_data.assign(m_buf.data(),rd); + Forward(m_data,rd/2); break; case ZT_LAW_MULAW: - data.convert(buf,"mulaw","slin",rd); - Forward(data,rd); + m_data.convert(m_buf,"mulaw","slin",rd); + Forward(m_data,rd); break; case ZT_LAW_ALAW: - data.convert(buf,"alaw","slin",rd); - Forward(data,rd); + m_data.convert(m_buf,"alaw","slin",rd); + Forward(m_data,rd); break; } } @@ -860,6 +864,7 @@ void ZapSource::run() break; } } + Debug(DebugAll,"ZapSource at EOF (read %d)",rd); } void ZapConsumer::Consume(const DataBlock &data, unsigned long timeDelta) @@ -885,15 +890,17 @@ void ZapConsumer::Consume(const DataBlock &data, unsigned long timeDelta) } if (m_buffer.length()+blk.length() <= m_bufsize*4) m_buffer += blk; +#ifdef DEBUG else Debug("ZapConsumer",DebugAll,"Skipped %u bytes, buffer is full",blk.length()); +#endif if (m_buffer.null()) return; if (m_buffer.length() >= m_bufsize) { int wr = ::write(fd,m_buffer.data(),m_bufsize); if (wr < 0) { if ((errno != EAGAIN) && (errno != EINTR)) - Debug(DebugFail,"ZapConsumer write error %d: %s", + Debug(DebugGoOn,"ZapConsumer write error %d: %s", errno,::strerror(errno)); } else { @@ -967,9 +974,16 @@ void ZapChan::idle() void ZapChan::restart() { disconnect(); + close(); + ::pri_reset(m_span->pri(),m_chan); +} + +void ZapChan::close() +{ setSource(); setConsumer(); - ::pri_reset(m_span->pri(),m_chan); + zt_close(m_fd); + m_fd = -1; } bool ZapChan::open(int defLaw) @@ -1002,11 +1016,8 @@ bool ZapChan::open(int defLaw) Debug(DebugInfo,"Opened Zap channel %d, law is: %s (fallback)",m_abschan,lookup(m_law,dict_str2ztlaw,"unknown")); return true; } - setSource(); - setConsumer(); - zt_close(m_fd); - m_fd = -1; - Debug(DebugFail,"Unable to set zap to any known format"); + close(); + Debug(DebugWarn,"Unable to set zap to any known format"); return false; } @@ -1044,10 +1055,7 @@ void ZapChan::hangup(int cause) Engine::enqueue(m); } disconnect(); - setSource(); - setConsumer(); - zt_close(m_fd); - m_fd = -1; + close(); } void ZapChan::sendDigit(char digit) @@ -1066,7 +1074,7 @@ bool ZapChan::call(Message &msg, const char *called) called = msg.getValue("called"); Debug("ZapChan",DebugInfo,"Calling '%s' on channel %d span %d", called, m_chan,m_span->span()); - int layer1 = lookup(msg.getValue("dataformat"),dict_str2law,0); + int layer1 = lookup(msg.getValue("format"),dict_str2law,-1); hangup(PRI_CAUSE_PRE_EMPTED); DataEndpoint *dd = static_cast(msg.userData()); if (dd) { @@ -1080,6 +1088,14 @@ bool ZapChan::call(Message &msg, const char *called) break; } open(dataLaw); + switch (m_law) { + case ZT_LAW_ALAW: + layer1 = PRI_LAYER_1_ALAW; + break; + case ZT_LAW_MULAW: + layer1 = PRI_LAYER_1_ULAW; + break; + } connect(dd); } else @@ -1119,7 +1135,7 @@ bool ZapHandler::received(Message &msg) if (!dest.matches(r)) return false; if (!msg.userData()) { - Debug(DebugFail,"Zaptel call found but no data channel!"); + Debug(DebugWarn,"Zaptel call found but no data channel!"); return false; } String chan = dest.matchString(1); diff --git a/yate.8 b/yate.8 index 58a755a5..480a0e3f 100644 --- a/yate.8 +++ b/yate.8 @@ -40,6 +40,9 @@ Quieter debugging (you can use more than once) .B \-d Daemonify, suppress output unless logged .TP +.B \-s +Supervised, restart if crashes or locks up +.TP .B \-l filename Log to file .SS Debugging (may not be compiled in) diff --git a/yatengine.h b/yatengine.h index cd541cee..68c403f9 100644 --- a/yatengine.h +++ b/yatengine.h @@ -950,7 +950,7 @@ private: * The Time class holds a time moment with microsecond accuracy * @short A time holding class */ -class Time : public GenObject +class Time { public: /**