On Mon, 15 Jun 2009, Mindaugas Kavaliauskas wrote:

Hi,

> I know that thread count could be changed between HB_MUTEXWAITERSCOUNT()  
> and actual HB_THREADSTART(), but I do not care a lot about it. But it would 
> be bad if this race condition can cause return garbage on some systems. In 
> this case it need to be protected by mutex. It could be renamed to 
> __MUTEX*() of couse, but I feel, that sometimes it could be useful to have 
> mutex information function. I found two things useful: waiters count and 
> queue length. Maybe we can create some more general mutex protected 
> function like:
>    HB_MUTEXQUEUEINFO( hMutex, [ @nWaitersCount ], [ @nQueueLength ] )
> or another name similar name.

The race condition in this case is not hardware problem (we do not need
additional synchronization mechanism to protect pMutex->waiters at least
on machines for which Harbour can be ported) but logical one.
Just simply the returned value does not give any precise information
which can be used in some algorithms which need strict number of waiters.
For programmer it's only approximated number of waiters. Of course such
information can also be usable but only if used algorithm does not need
strict value.
Due to missing synchronization between taking value and checked event
it can return bigger or smaller value then the one which programmer expect.
It can be well seen in your code:

  DO WHILE .T.
    IF (nI := socket_select({Self:hListen},,, 1000)) > 0
      hSocket := socket_accept(Self:hListen)
      nWaiters := hb_mutexWaitersCount(Self:hmtxQueue) /* (*) */
      IF nWaiters < 2 .AND. LEN(aThreads) < THREAD_COUNT_MAX
  /*
     We need two threads in worst case. If first thread becomes a sessioned
     thread, the second one will continue to serve sessionless requests for
     the same connection. We create two threads here to avoid free thread
     count check (and aThreads variable sync) in ProcessRequest().
  */
        AADD(aThreads, hb_threadStart(@ProcessConnection(), Self))
        AADD(aThreads, hb_threadStart(@ProcessConnection(), Self))
      ENDIF
      hb_mutexNotify(Self:hmtxQueue, {hSocket, ""})
    ENDIF
  ENDDO

Please note that in fact it's not guarantied that you will create such
two threads for each connection. This code can create more or less threads.
I.e. it's possible that only two threads will be created for 100 connections.
Just simply two new threads created for 1-st connection are stopped inside
ProcessConnection() at:
   hb_mutexSubscribe(oServer:hmtxQueue,, @aI)
and not woken up immediately after:
   hb_mutexNotify(Self:hmtxQueue, {hSocket, ""})
then main thread receives next 99 connections and executes
   hb_mutexNotify(Self:hmtxQueue, {hSocket, ""})
for each of them. It may happen because it's not guarantied that OS wakes
up waiting thread immediately after notification. I know that the chance
is very small but users used to forget about such things and create code
which needs some strict time condition. On one computer such code can work
for a year without any problems on some others it may crash immediately.
In this case it's also possible that some threads will never serve incoming
connection because threads created for previous one will have finish his
job and took new one before them. It will be enough that OS will not execute
new thread immediately after hb_threadStart() or freeze them just before
hb_mutexSubscribe().
I haven't analyzed the whole code yet but I guess that in this case nothing
very wrong will happen. Just simply the server may not be as efficient as
expected due to not perfect approximation returned by hb_mutexWaitersCount().
The function name is less important here and using '__' is even more
confusing because it suggests that it's some internal function. It's not
the core of problem. hb_mutexWaitersCount() cannot be used as atomic
function to synchronize any code because there is always race condition
which effects returned value which can be bigger (mutex is notified but
threads not woken up yet) or smaller (some threads are frozen before
incrementing pMutex->waiters). It's important that programmers understand
it. That's all.

> Przemek, I think your opinion is most valuable here. Or maybe you propose a 
> completely different problem solution. The problem is worker threads count 
> management: if too many workers are free, it would be good to decrease 
> workers count; if job queue length is too big, it would be good to start 
> new worker threads.

In this case you will have better approximation if you also use the size
of message queue and calculate the difference between pMutex->waiters
and hb_arrayLen( pMutex->events ) (accessing pMutex->events should be
covered by lock) and proposed by you HB_MUTEXQUEUEINFO() gives such
information. But it's also only approximation and can be used only to
tune some parameters not as direct synchronization mechanism.
To help you in this case I will have to carefully look at the uhttpd2
code. When I'll try to make it when I'll find some spare time.

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

Reply via email to