Hi Mindaugas,

> thanks for a great sample code. It is great because it makes me to ask a 
> few questions, and to understand the details of process sync.
> 
> There are  things I do not like in this sample, and I'd like to make it 
> more perfect. The first thing I want to optimize is loop containing 
> sleep(0.01). This thread will require CPU 100 times per second, but 
> maybe my background task will be executed only every 5 minutes. I do not 
> want to waste a CPU time.

Exactly. It's not efficient. It was only an example.

> So, the first question is, can I optimize my loop to avoid time checking 
> 100 times per second. I've tried to do this way:
>     static s_nDays := 0
>     static function hb_backgroundThread()
>        local n, nTime, nMin
>        while !s_lStop
>           hb_mutexLock( s_mutex )
>           nTime := seconds() + s_nDays
>           if nTime < s_nLastTime
>              nTime += 24*60*60
>              s_nDays += 24*60*60
>           endif
>           s_nLastTime = nTime
>           nMin := nTime + 24*60*60
>           nTime *= 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_mutexUnlock( s_mutex )
>           hb_idleSleep( nMin / 1000 )
>        enddo
>     return nil

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().

> The second thing I want to implement is background thread exit, i.e., I 
> want to send some signal/mutex/semaphore/event from main thread to the 
> background thread and wait for exit using hb_threadJoin(). I'll need 
> some method to interrupt (possibly long) sleep.

when background thread is created store value returned by hb_startThread()
in static s_thread variable and then add this function:

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

> The other problem is, how to add new background tasks? I will also need 
> to interrupt sleep, because the execution time for new task can be 
> earlier than a time for existing tasks.
> So, how can I implement an interruptable sleep and all required 
> synchronization objects?

hb_idleSleep() is not good choice in such case. Use hb_mutexSubscribe()
with timeout parameter, f.e.:
   hb_mutexSubscribe( s_mutex, nMin / 1000 )

and then you can wake up the thread waiting on mutex by simple
   hb_mutexNotify( s_mutex )

You should also change hb_backgroundExit() to wake up the thread:
   function hb_backgroundExit()
      if s_thread != NIL
         s_lStop := .t.
         hb_mutexNotify( s_mutex )
         hb_threadJoin( s_thread )
      endif
   return nil


hb_mutexSubscribe() has yet another internal feature. When you call it
and thread locked the mutex before then inside this function when thread
is suspended mutex is automatically unlocked and then locked again before
leaving hb_mutexSubscribe()
It means that you can simplify then while loop above, f.e.:

      hb_mutexLock( s_mutex )
      while !s_lStop
         [...]
         hb_mutexSubscribe( s_mutex, nMin / 1000 )
      enddo
      hb_mutexUnlock( s_mutex )

Please also remember that hb_mutexSubscribe() can return immediately if
some thread pushed some notification object before you call it.
If you want to drop all previous notifications waiting in the mutex
queue then you can use hb_mutexSubscribeNow() but please think if it
does not cause problems for your code and some important events are
not lost in such case.
If you want then you can also use some values in hb_mutexNotify()
and receive it as 3-rd parameter of hb_mutexSubscribe() to mark some
actions. F.e. instead of using s_stop variable you can pass .T. as
2-nd hb_mutexNotify() parameter and then main loop you can look like:

      hb_mutexLock( s_mutex )
      while .t.
         [...]
         if hb_mutexSubscribe( s_mutex, nMin / 1000, @xResult )
            if valtype( xResult ) == "L" and xResult
               exit
            endif
         endif
      enddo
      hb_mutexUnlock( s_mutex )

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

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

Reply via email to