Hi,

Fine but I suggest to create separate function to extract number of
seconds from some starting day, f.e.:
   HB_FUNC( HB_MILLISEC )
   {
      hb_retnin( hb_dateMilliSeconds() );
   }

and use it instead of seconds().

Yes, it would be great.


In harbour/tests/mt/ you have simple demonstration programs which shows
different synchronization mechanisms. They are not only tests.

Thanks, for the answer. I've analysed /tests/mt, but I do not have skills of MT programming. So, I expected we will need two more mutexes for exit interrupt and for add new task interrupt. And I was unable to imagine how can I wait for multiple events, something like:
   hb_mutexSubscribe( {s_mutex, s_mutexExit, s_mutexAdd}, nMin/1000 )
But things become easy after you put a hand on it :)

So,I assume our current code is:

static function hb_backgroundThread()
   local n, nTime, nMin
   hb_mutexLock( s_mutex )
   while !s_lStop
      nTime := hb_MilliSeconds()
      nMin := nTime + 24*60*60*1000
      for n := 1 to s_nCounter
         // {nHandle, bAction, nMilliSec, lActive, nNextTime}
         if s_aTasks[ n, 4 ] .and. s_aTasks[ n, 5 ] <= nTime
            eval( s_aTasks[ n, 2 ] )
            s_aTasks[ n, 5 ] += s_aTasks[ n, 3 ]
         endif
         nMin := MIN( nMin, s_aTasks[ n, 5 ] )
      next
      hb_mutexSubscribe( s_mutex, nMin / 1000 )
   enddo
   hb_mutexUnlock( s_mutex )
return nil

function hb_backgroundExit()
   if s_thread != NIL
      s_lStop := .t.
      hb_mutexNotify( s_mutex )
      hb_threadJoin( s_thread )
   endif
return nil

static function hb_backgroundInit()
   if s_lStop
      hb_threadOnce( @s_onceControl, {||
            s_mutex := hb_mutexCreate()
            hb_mutexLock( s_mutex )
            s_lStop := .f.
            s_thread := hb_threadStart( @hb_backgroundThread() )
            hb_mutexUnlock( s_mutex )
            return nil
         } )
   endif
return nil

function hb_backgroundAdd( bAction, nMilliSec, lActive )
   local nHandler
   if !HB_ISNUMERIC( nMilliSec )
      nMilliSec := 0
   endif
   if ! HB_ISLOGICAL( lActive )
      lActive := .t.
   endif
   hb_backgroundInit()
   hb_mutexLock( s_mutex )
aadd( s_aTasks, { nHandler := ++s_nCounter, bAction, nMilliSec, lActive, hb_MilliSeconds() + nMilliSec } )
   hb_mutexUnlock( s_mutex )
return nHandler


and I still have two questions...


1) s_lStop access is synchronized using s_mutex. Is hb_backgroundExit() code OK? Don't we need to change to:

function hb_backgroundExit()
   if s_thread != NIL
      hb_mutexLock( s_mutex )
      s_lStop := .t.
      hb_mutexNotify( s_mutex )
      hb_mutexUnLock( s_mutex )
      hb_threadJoin( s_thread )
   endif
return nil

otherwise s_lStop can be assigned (by hb_backgroundExit) and accessed (by hb_backgroundThread) simultaneous and give unpredictable results.

And here is a new question (well, 3 new questions...):
1a) is it OK to call hb_mutexNotify() if mutex is locked?
1b) will hb_mutexSubscribe() exit after hb_mutexUnLock() only? (because it tries to lock mutex before exit)
1c) if 1b answer is "yes", then code:
      hb_mutexLock( s_mutex )
      s_lStop := .t.
      hb_mutexNotify( s_mutex )
      hb_threadJoin( s_thread )
      hb_mutexUnLock( s_mutex )
will create deadlock. hb_threadJoin() will wait for child forever, but child does not return from hb_mutexSubscribe() because trying to lock mutex. Am I right?


The second question is:
2) I assume hb_backgroundAdd() could be called from any thread, and hb_backgroundExit() is called from the main thread on application exit. It is possible that s_thread != NIL will be false for the main thread, but another thread is executing hb_backgroundAdd()/hb_backgroundInit() that time. Let's say some code like:
   s_mutex := hb_mutexCreate()
In this case the new thread will be started, but main thread will forget to join it.

2a) do we need a mutex to protect s_thread access/assign?
2b) can we manage to use the same s_mutex, or we will need s_mutex2?
2c) where s_mutex2 should be initialized?
2d) I see STATIC s_mutex2 := hb_mutexCreate() in one of your last commits. So, perhaps 2c answer is "Use STATIC initialization". Is it possible to avoid static initialization? Or we will need one more s_mutex3 to protect s_mutex2 initialization and this will be never-ending story without STATICs.


Best regards,
Mindaugas
_______________________________________________
Harbour mailing list
Harbour@harbour-project.org
http://lists.harbour-project.org/mailman/listinfo/harbour

Reply via email to