Esempio n. 1
0
void release()
{
	if(g_clients!=NULL)
	{
		logD(TAG," release g_clients");
		releaseClientArray();
		g_clients=NULL;
	}

	if(g_context!=NULL)
	{
		logD(TAG," release g_context");
		if(g_context->pServer!=NULL)
		{
			if(g_context->pServer->listen_ip!=NULL)
			{
				free(g_context->pServer->listen_ip);
			}

			if(g_context->pServer->name!=NULL)
			{
				free(g_context->pServer->name);
			}
			memset(g_context->pServer,0,sizeof(TGServer));

			free(g_context->pServer);
		}
		if(g_context->listenfd>=0)
		{
#ifdef LINUX
			shutdown(g_context->listenfd,SHUT_RDWR);
#endif
			close(g_context->listenfd);
		}

		if(g_context->localAdminfd>=0)
		{
			close(g_context->localAdminfd);
		}
		memset(g_context,0,sizeof(TGContext));
		free(g_context);
		g_context=NULL;
	}
	clients_ar_size=0;
	logD(TAG," finish release ");
}
Esempio n. 2
0
bool
RecpathConfig::IsPathExist(const std::string & path)
{
    logD(recpath, _func_);

    //m_mutex.lock();

    ConfigMap::iterator itr = m_configs.find(path);
    bool bRes = (itr != m_configs.end());
    if(bRes)
        logD(recpath, _func_, "path [", path.c_str(), "] exist");
    else
        logD(recpath, _func_, "path [", path.c_str(), "] doesnt exist");

    //m_mutex.unlock();

    return bRes;
}
Esempio n. 3
0
void MModLoader::setVars(IVars *v) {
	IV_LOGGER = reinterpret_cast<ILogger*>(v->get("eLogger"));

	IV_VARS = v;
	IV_SETT = reinterpret_cast<ISettings*>(v->get("eSettings"));
	IV_SETC = reinterpret_cast<ISettCont*>(v->get("eSettCont"));

	MV_CORE_MODS = new MCoreMods();
	MV_MODS = new MMods();

	logD("ModLoader initialized");
}
Esempio n. 4
0
Time
Timers::getSleepTime_microseconds ()
{
    Time const cur_time = getTimeMicroseconds ();

  MutexLock l (&mutex);

    TimerChain * const chain = expiration_tree_leftmost;
    if (chain == NULL) {
	logD (timers, _func, ": null chain");
	return (Time) -1;
    }

    if (chain->nearest_time <= cur_time) {
	logD (timers, _func, ": now");
	return 0;
    }

    logD (timers, _func, ": nearest: ", chain->nearest_time, ", cur: ", cur_time, ", delta: ", chain->nearest_time - cur_time);

    return chain->nearest_time - cur_time;
}
Esempio n. 5
0
std::string RecpathConfig::GetNextPath(const std::string & prev_path)
{
    logD(recpath, _func_);

    //m_mutex.lock();

    if(m_configs.size() == 0)
    {
        //m_mutex.unlock();
        logD(recpath, _func_, "Config is empty");
        return std::string("");
    }

    std::string next_path;
    if(prev_path.size())
    {
        logD(recpath, _func_, "prev_path is ", prev_path.c_str());
        ConfigMap::iterator itr = m_configs.find(std::string(prev_path));
        if(itr == m_configs.end())
        {
            // we dont know last path, so dont know next, so return first
            next_path = m_configs.begin()->first;
            logD(recpath, _func_, "prev_path not exist, so return first path [", next_path.c_str(), "]");
        }
        else
        {
            itr++;
            if(itr == m_configs.end())
            {
                // that was last path in list, so return empty path
                next_path = "";
                logD(recpath, _func_, "prev_path is last path, so return empty path");
            }
            else
            {
                // return next path
                next_path = itr->first;
                logD(recpath, _func_, "return next path [", next_path.c_str(), "]");
            }
        }
    }
    else
    {
        // if there's no prev path, so return first
        ConfigMap::iterator itr1 = m_configs.begin();
        next_path = itr1->first;
        logD(recpath, _func_, "return first path [", next_path.c_str(), "]");
    }

    //m_mutex.unlock();

    return next_path;
}
Esempio n. 6
0
void loadService(const char * path)
{
	if(path==NULL || strlen(path)==0)
	{
		logN(TAG ,LOG_LEVEL_WARNING," path is empty");
		return;
	}

	logI(TAG," prepare to load service from library:%s",path);
	
	void * dlHandler=NULL;
	char * errmsg;
#ifdef LINUX
	dlHandler=dlopen(path,RTLD_NOW);
	if(dlHandler==NULL)
	{
		errmsg=dlerror();
		logN("SERVICE",LOG_LEVEL_WARNING," can not open library:%s %s",path,errmsg);
		return;
	}
#elif WIN32
#endif
	
	struct TGService * pService=NULL;
	if(checkLib(dlHandler,&pService)==0)
	{
		logD(TAG,"check lib successfully");
		//FIXME use system seportor
		char * pLibName=NULL;
		getLibName(path,&pLibName);
		if(pLibName==NULL)
		{
			pLibName=(char *)malloc(sizeof(5));	
			memset(pLibName,0,10);
			sprintf(pLibName,"%d",pService->sID);
		}
		
		pService->serviceName=pLibName;
		addService(pService);
	}
	else
	{
		logW(TAG,"check library error");
	}

}
Esempio n. 7
0
void getLibName(char * path,char ** ppName)
{
	if(path!=NULL)
	{
		int size=strlen(path)+1;
		char buf[size];
		memset(buf,0,size);
		memcpy(buf,path,size-1);
		buf[size-1]='\0';
		//FIXME should use maco of system
		int pos=lastFindSeparator(buf,"/");	
		if(pos>=0)
		{
			int nsize=size-pos;	
			char * pc=(char *) malloc(nsize);
			memset(pc,0,nsize);
			strncpy(pc,&buf[pos+1],nsize-1);
			*ppName=pc;
			logD("UTIL"," get lib name:%s\n",pc);
		}
	}
	
}
Esempio n. 8
0
// copy parameters to pSer
void setServiceCreateParameters(struct TGService * pSer,const char * para)
{
	if(pSer==NULL)
	{
		logE(TAG," service not exist");
		return;
	}

	if(pSer->parameters==NULL)
	{
		pSer->parCounts=0;
		int size=SERVICE_DEFAULT_PARA_COUNTS*sizeof(char *);
		pSer->parameters=(char **)malloc(size);
		memset(pSer->parameters,0,size);
	}
	// parameters is out of array need create new 
	else if(pSer->parCounts!=0 && (pSer->parCounts)%SERVICE_DEFAULT_PARA_COUNTS==0)
	{
		int size=(pSer->parCounts+SERVICE_DEFAULT_PARA_COUNTS)*sizeof(char *);
		logI(TAG," create new parameters for id:%d parCounts:%d", pSer->sID,pSer->parCounts);
		char ** pOld=pSer->parameters;
		pSer->parameters=(char **)malloc(size);
		memset(pSer->parameters,0,size);
		memcpy(pSer->parameters,pOld,pSer->parCounts*sizeof(char *));
		free(pOld);
	}

	char *pBuf=NULL;
	int bufSize=strlen(para)+1;
	pBuf=(char *)malloc(bufSize);
	memset(pBuf,0,bufSize);
	strncpy(pBuf,para,bufSize-1);
	pBuf[bufSize-1]='\0';
	pSer->parameters[pSer->parCounts++]=pBuf;
	logD(TAG,"  parameters:%d,  %s",pSer->parCounts,pSer->parameters);
	
}
Esempio n. 9
0
bool RecpathConfig::LoadConfig(const std::string & path_to_config)
{
    logD(recpath, _func_);
    //m_mutex.lock();

    m_configs.clear();
    m_bIsEmpty = true;

    Json::Value root;   // will contains the root value after parsing.
    Json::Reader reader;

    std::ifstream config_file(path_to_config, std::ifstream::binary);
    if(!config_file.good())
    {
        //m_mutex.unlock();
        logE_(_func_, "fail to load config");
        return false;
    }

    bool parsingSuccessful = reader.parse( config_file, root, false );
    if(!parsingSuccessful)
    {
        //m_mutex.unlock();
        logE_(_func_, "fail to parse config");
        return false;
    }

    Json::Value configs = root["configs"];
    if(configs.empty())
    {
        //m_mutex.unlock();
        logE_(_func_, "fail to find \"configs\" section");
        return false;
    }

    for( Json::ValueIterator itr = configs.begin() ; itr != configs.end() ; itr++ )
    {
        Json::Value value = (*itr);

        Json::Value path = value["path"];
//        Json::Value quota = value["quota"];
//        Json::Value mode = value["mode"];

        if(path.empty())// || quota.empty() || mode.empty())
        {
            m_configs.clear();
            //m_mutex.unlock();
            logE_(_func_, "fail to parse params for section No ", itr.index());
            return false;
        }

        m_configs[path.asString()] = ConfigParam();
    }

    // dump config in log
    int i = 0;
    for(ConfigMap::const_iterator it = m_configs.begin(); it != m_configs.end(); ++it)
    {
        logD(recpath, _func_, "PathEntry ", i++);
        logD(recpath, _func_, "Path: ", it->first.c_str());
//        logD(recpath, _func_, "Quota: ", it->second.quota);
//        logD(recpath, _func_, "Mode: ", it->second.write_mode);
    }
    m_configJson = root.toStyledString();

    m_bIsInit = true;

    //m_mutex.unlock();

    return true;
}
Esempio n. 10
0
std::string RecpathConfig::GetNextPathForStream()
{
    logD(recpath, _func_);

    //m_mutex.lock();

    if(m_configs.size() == 0)
    {
        //m_mutex.unlock();
        logD(recpath, _func_, "Config is empty");
        return std::string("");
    }
    else
        logD(recpath, _func_, "Config size is ", m_configs.size());

    std::string next_path;
    while(true)
    {
        for(ConfigMap::const_iterator it = m_configs.begin(); it != m_configs.end(); ++it)
        {
            Int64 freeSize = MemoryDispatcher::Instance().GetDiskFreeSizeFromDiskname(it->first);
            logD(recpath, _func_, "diskname is ", it->first.c_str());
            logD(recpath, _func_, "freeSize is ", freeSize);
            if(freeSize > MINFREESIZE)
            {
                next_path = it->first;
                logD(recpath, _func_, "next_path is ", next_path.c_str());
                break;
            }
        }

        if(next_path.size())
        {
            break;
        }
        else
        {
            logD(recpath, _func_, "next_path is empty");
        }

        // cleaning
        logD(recpath, _func_, "CLEANING");
        ChannelChecker::ChannelFileDiskTimes channelFileDiskTimes;
        std::map<std::string, WeakRef<FFmpegStream> >::iterator itFFStream = m_pStreams->begin();
        for(itFFStream; itFFStream != m_pStreams->end(); itFFStream++)
        {
            logD(recpath, _func_, "channel_name = ", itFFStream->first.c_str());
            Ref<ChannelChecker> channelChecker = itFFStream->second.getRefPtr()->GetChannelChecker();
            std::pair<std::string, ChChDiskTimes> oldestFileDiskTimes = channelChecker->GetOldestFileDiskTimes();
            logD(recpath, _func_, "filename = ", oldestFileDiskTimes.first.c_str());
            logD(recpath, _func_, "diskname = ", oldestFileDiskTimes.second.diskName.c_str());
            logD(recpath, _func_, "beginTime = ", oldestFileDiskTimes.second.times.timeStart);
            logD(recpath, _func_, "endTime = ", oldestFileDiskTimes.second.times.timeEnd);
            if(oldestFileDiskTimes.first.size())
            {
                channelFileDiskTimes.insert(oldestFileDiskTimes);
            }
        }

        // find most older file
        int minTime = std::numeric_limits<int>::max();
        std::string fileName;
        std::string diskName;
        ChannelChecker::ChannelFileDiskTimes::iterator iter = channelFileDiskTimes.begin();
        for(iter; iter != channelFileDiskTimes.end(); iter++)
        {
            if(minTime > iter->second.times.timeStart)
            {
                minTime = iter->second.times.timeStart;
                fileName = iter->first;
                diskName = iter->second.diskName;
            }
        }

        logD(recpath, _func_, "minTime = ", minTime);
        logD(recpath, _func_, "minfileName = ", fileName.c_str());
        logD(recpath, _func_, "mindiskName = ", diskName.c_str());

        // delete it (if filename is not recorded at current time)
        StRef<String> dir_name = st_makeString(diskName.c_str());
        StRef<String> file_name = st_makeString(fileName.c_str());
        StRef<String> full_file_name = st_makeString(dir_name, "/", file_name, ".flv");
        if(!MemoryDispatcher::Instance().IsFilenameRecorded(full_file_name->cstr()))
        {
            Ref<Vfs> vfs = Vfs::createDefaultLocalVfs (dir_name->mem());

            StRef<String> const filenameFull = st_makeString(fileName.c_str(), ".flv");

            logD(recpath, _func_, "filenameFull to remove = ", filenameFull);

            vfs->removeFile (filenameFull->mem());
            vfs->removeSubdirsForFilename (filenameFull->mem());

            std::string channel_name = fileName.substr(0,fileName.find("/"));
            (*m_pStreams)[channel_name].getRefPtr()->GetChannelChecker()->DeleteFromCache(diskName, fileName);
        }
        else
        {
            break;
        }
    }

    //m_mutex.unlock();

    return next_path;
}
Esempio n. 11
0
mt_throws Result updateTime ()
{
    LibMary_ThreadLocal * const tlocal = libMary_getThreadLocal();

#ifdef LIBMARY_PLATFORM_WIN32
    DWORD const win_time_dw = timeGetTime();
    if (tlocal->prv_win_time_dw >= win_time_dw) {
        tlocal->win_time_offs += 0x100000000;
    }
    tlocal->prv_win_time_dw = win_time_dw;
    DWORD const win_time = win_time_dw + tlocal->win_time_offs;

    Time const new_seconds = (Time) win_time / 1000;
    Time const new_microseconds = (Time) win_time * 1000;
#else
  #ifdef __MACH__
    mach_timespec_t mts;
    {
        #warning TODO call host_get_clock_service only once
        clock_serv_t clk;
        host_get_clock_service (mach_host_self(), SYSTEM_CLOCK, &clk);

        clock_get_time (clk, &mts);

        mach_port_deallocate (mach_task_self(), clk);
    }

    Time const new_seconds = mts.tv_sec;
    Time const new_microseconds = (Uint64) mts.tv_sec * 1000000 + (Uint64) mts.tv_nsec / 1000;

#if 0
// Deprecated
    gint64 const mono_time = g_get_monotonic_time ();
    Int64 const mono_time = 0;
    Time const new_seconds = mono_time / 1000000;
    Time const new_microseconds = mono_time;
#endif
  #else
    struct timespec ts;
    // Note that clock_gettime is well-optimized on Linux x86_64 and does not carry
    // full syscall overhead (depends on system configuration).
    int const res = clock_gettime (CLOCK_MONOTONIC, &ts);
    if (res == -1) {
	exc_throw (PosixException, errno);
	exc_push (InternalException, InternalException::BackendError);
	logE_ (_func, "clock_gettime() failed: ", errnoString (errno));
	return Result::Failure;
    } else
    if (res != 0) {
	exc_throw (InternalException, InternalException::BackendError);
	logE_ (_func, "clock_gettime(): unexpected return value: ", res);
	return Result::Failure;
    }

    logD (time, _func, "tv_sec: ", ts.tv_sec, ", tv_nsec: ", ts.tv_nsec);

    Time const new_seconds = ts.tv_sec;
    Time const new_microseconds = (Uint64) ts.tv_sec * 1000000 + (Uint64) ts.tv_nsec / 1000;
  #endif
#endif

    tlocal->time_log_frac = new_microseconds % 1000000 / 100;

    if (new_seconds >= tlocal->time_seconds)
	tlocal->time_seconds = new_seconds;
    else
	logW_ (_func, "seconds backwards: ", new_seconds, " (was ", tlocal->time_seconds, ")");

    if (new_microseconds >= tlocal->time_microseconds)
	tlocal->time_microseconds = new_microseconds;
    else
	logW_ (_func, "microseconds backwards: ", new_microseconds, " (was ", tlocal->time_microseconds, ")");

    logD (time, _func, fmt_hex, tlocal->time_seconds, ", ", tlocal->time_microseconds);

    if (tlocal->saved_monotime < tlocal->time_seconds
        || tlocal->saved_unixtime == 0)
    {
	// Updading saved unixtime once in a minute.
	if (tlocal->time_seconds - tlocal->saved_monotime >= 60
            || tlocal->saved_unixtime == 0)
        {
	    // Obtaining current unixtime. This is an extra syscall.
	    tlocal->saved_unixtime = time (NULL);
	    tlocal->saved_monotime = tlocal->time_seconds;
	}

      // Updating localtime (broken-down time).

	time_t const cur_unixtime = tlocal->saved_unixtime + (tlocal->time_seconds - tlocal->saved_monotime);
	tlocal->unixtime = cur_unixtime;
	// Note that we call tzset() in libMary_posixInit() for localtime_r() to work correctly.
#ifdef LIBMARY_PLATFORM_WIN32
  #ifdef LIBMARY_WIN32_SECURE_CRT
        if (localtime_s (&tlocal->localtime, &cur_unixtime) != 0)
            logF_ (_func, "localtime_s() failed");
  #else
        libraryLock ();
        struct tm * const tmp_localtime = localtime (&cur_unixtime);
        if (tmp_localtime) {
            tlocal->localtime = *tmp_localtime;
        }
        libraryUnlock ();
        if (!tmp_localtime)
            logF_ (_func, "localtime() failed");
  #endif
#else
        // TODO FIXME This lib function is dog-slow and has huge synchronization overhead.
	localtime_r (&cur_unixtime, &tlocal->localtime);
#endif
    }

//    long const timezone_abs = timezone >= 0 ? timezone : -timezone;
    long const timezone_abs = 0;
//    tlocal->timezone_str [0] = timezone >= 0 ? '+' : '-';
    tlocal->timezone_str [0] = '+';
    tlocal->timezone_str [1] = '0' + timezone_abs / 36000;
    tlocal->timezone_str [2] = '0' + timezone_abs /  3600 % 10;
    tlocal->timezone_str [3] = '0' + timezone_abs /   600 %  6;
    tlocal->timezone_str [4] = '0' + timezone_abs /    60 % 10;

    return Result::Success;
}
Esempio n. 12
0
mt_sync_domain (readTask) bool
GetFileSession::readTask (void * const _self)
{
    GetFileSession * const self = static_cast <GetFileSession*> (_self);

    logD (getfile, _func_);

    if (self->session_state == SessionState_Header) {
        MOMENT_SERVER__HEADERS_DATE

        for (;;) {
            MediaReader::ReadFrameResult const res = self->media_reader->readMoreData (&read_frame_backend, self);
            if (res == MediaReader::ReadFrameResult_Failure) {
                logE_ (_func, "ReadFrameResult_Failure");

                ConstMemory msg = "Data retrieval error";
                self->sender->send (self->page_pool,
                                    true /* do_flush */,
                                    // TODO No cache
                                    MOMENT_SERVER__500_HEADERS (msg.len()),
                                    "\r\n",
                                    msg);

                if (!self->req_is_keepalive)
                    self->sender->closeAfterFlush ();

                logA_ ("mod_nvr 500 ", self->req_client_addr, " ", self->req_request_line);
                return false /* do not reschedule */;
            }

            bool header_done = false;
            if (res == MediaReader::ReadFrameResult_NoData) {
                logD (getfile, _func, "ReadFrameResult_NoData");

                if (!self->got_last_audio_ts &&
                    !self->got_last_video_ts)
                {
                    ConstMemory msg = "Requested video data not found";
                    self->sender->send (self->page_pool,
                                        true /* do_flush */,
                                        // TODO No cache
                                        MOMENT_SERVER__404_HEADERS (msg.len()),
                                        "\r\n",
                                        msg);

                    if (!self->req_is_keepalive)
                        self->sender->closeAfterFlush ();

                    logA_ ("mod_nvr 404 ", self->req_client_addr, " ", self->req_request_line);
                    return false /* do not reschedule */;
                }

                header_done = true;
            } else
            if (res == MediaReader::ReadFrameResult_Finish) {
                logD (getfile, _func, "ReadFrameResult_Finish");
                header_done = true;
            }

            if (header_done) {
                self->session_state = SessionState_Data;
                self->media_reader->reset ();
                break;
            }

            assert (res != MediaReader::ReadFrameResult_BurstLimit);
            assert (res == MediaReader::ReadFrameResult_Success);
        }

        PagePool::PageListInfo const mp4_header = self->mp4_muxer.pass1_complete (self->duration_sec * 1000);

        {
            self->sender->send (self->page_pool,
                                true /* do_flush */,
                                // TODO No cache
                                MOMENT_SERVER__OK_HEADERS (
                                        (!self->octet_stream_mime ? ConstMemory ("video/mp4") :
                                                                    ConstMemory ("application/octet-stream")),
                                        mp4_header.data_len + self->mp4_muxer.getTotalDataSize()),
                                "\r\n");
            logD_ (_func, "CONTENT-LENGTH: ", mp4_header.data_len + self->mp4_muxer.getTotalDataSize());

            if (!self->req_is_keepalive)
                self->sender->closeAfterFlush ();

            logA_ ("mod_nvr 200 ", self->req_client_addr, " ", self->req_request_line);
        }

        {
            SenderMessageEntry_Pages * const msg_pages = SenderMessageEntry_Pages::createNew (/*header_len=*/ 0);
            msg_pages->init (mp4_header.first, self->page_pool, /*msg_offs=*/ 0, mp4_header.data_len);

            self->sender->sendMessage (msg_pages, true /* do_flush */);
        }

        self->transfer_start_time_millisec = getTimeMilliseconds();
        self->bytes_transferred += mp4_header.data_len;

        self->sender->getEventInformer()->subscribe (
                CbDesc<Sender::Frontend> (&sender_frontend, self, self));
    }
Esempio n. 13
0
Timers::TimerKey
Timers::addTimer_microseconds (CbDesc<TimerCallback> const &cb,
			       Time const time_microseconds,
			       bool const periodical,
                               bool const auto_delete,
                               bool const delete_after_tick)
{
    logD (timers, _func, "time_microseconds: ", time_microseconds);

    Timer * const timer = new (std::nothrow) Timer (this, cb);
    assert (timer);
    timer->periodical = periodical;
    timer->delete_after_tick = delete_after_tick;
    timer->due_time = getTimeMicroseconds() + time_microseconds;
    if (timer->due_time < time_microseconds) {
	logW_ (_func, "Expiration time overflow");
	timer->due_time = (Time) -1;
    }

    if (auto_delete && cb.coderef_container) {
        timer->del_sbn = cb.coderef_container->addDeletionCallback (
                CbDesc<Object::DeletionCallback> (subscriberDeletionCallback,
                                                  timer,
                                                  getCoderefContainer()));
    }

    logD (timers, _func, "getTimeMicroseconds(): ", getTimeMicroseconds(), ", due_time: ", timer->due_time);

    mutex.lock ();

    bool first_timer = false;

    TimerChain *chain = interval_tree.lookup (time_microseconds);
    if (!chain) {
	chain = new (std::nothrow) TimerChain;
        assert (chain);
	chain->interval_microseconds = time_microseconds;
	chain->nearest_time = timer->due_time;

	if (expiration_tree.isEmpty()
	    || expiration_tree_leftmost->nearest_time > chain->nearest_time)
	{
	    first_timer = true;
	}

	interval_tree.add (chain);
	expiration_tree.add (chain);
	expiration_tree_leftmost = expiration_tree.getLeftmost();
    }

    timer->chain = chain;
    chain->timer_list.append (timer);

    mutex.unlock ();

    if (first_timer) {
	logD (timers, _func, "calling first_added_cb()");
	first_added_cb.call_ ();
    }

    return timer;
}
Esempio n. 14
0
void
Timers::processTimers ()
{
    Time const cur_time = getTimeMicroseconds ();

    mutex.lock ();

    TimerChain *chain = expiration_tree_leftmost;
    if (chain == NULL) {
	mutex.unlock ();
	return;
    }

    while (!chain->timer_list.isEmpty () && chain->nearest_time <= cur_time) {
	Time const cur_nearest_time = chain->nearest_time;

	logD (timers, _func, "cur_nearest_time: ", cur_nearest_time, ", cur_time: ", cur_time);

	Timer * const timer = chain->timer_list.getFirst ();
	assert (timer->active);
	chain->timer_list.remove (timer);

        bool delete_timer = false;
	if (timer->periodical) {
	    timer->due_time += chain->interval_microseconds;
	    if (timer->due_time < chain->interval_microseconds) {
		logW_ (_func, "Expiration time overflow");
		timer->due_time = (Time) -1;
	    }
	    chain->timer_list.append (timer);
	} else {
	    timer->active = false;

            if (timer->delete_after_tick) {
                if (timer->del_sbn) {
                    // TODO We create CodeRef twice: here and in call_unlocks_mutex_().
                    //      Should do this only once for efficiency.
                    CodeRef const code_ref = timer->timer_cb.getWeakCodeRef();
                    // If 'code_ref' is null, then doDeleteTimer() will be called
                    // by subscriberDeletionCallback(), which is likely just about
                    // to be called.
                    if (code_ref) {
                        code_ref->removeDeletionCallback (timer->del_sbn);
                        delete_timer = true;
                    }
                } else {
                    delete_timer = true;
                }
            }
	}

	bool delete_chain;
	if (chain->timer_list.isEmpty ()) {
	    expiration_tree.remove (chain);
	    expiration_tree_leftmost = expiration_tree.getLeftmost();
	    interval_tree.remove (chain);
	    delete_chain = true;
	} else {
	    if (cur_nearest_time != chain->timer_list.getFirst ()->due_time) {
		expiration_tree.remove (chain);
		chain->nearest_time = chain->timer_list.getFirst ()->due_time;
		expiration_tree.add (chain);
		expiration_tree_leftmost = expiration_tree.getLeftmost();
	    }
	    delete_chain = false;
	}

	timer->timer_cb.call_unlocks_mutex_ (mutex);

      // 'timer' might have been deleted by the user and should not be used
      // directly anymore.
      //
      // We can't delete the timer ourselves here for a similar reason: its
      // lifetime is controlled by the user, so we can't tie it to callback's
      // weak_obj.

        if (delete_timer)
            delete timer;

	if (delete_chain)
	    delete chain;

	mutex.lock ();

	// 'chain' might have been deleted due to user's actions in 'timer_cb' callback.
	chain = expiration_tree_leftmost;
	if (chain == NULL) {
	    mutex.unlock ();
	    return;
	}
    }

    mutex.unlock ();
}