xdasd_thread.c

Go to the documentation of this file.
00001 /*----------------------------------------------------------------------------
00002  * Copyright (c) 2006, Novell, Inc.
00003  * All rights reserved.
00004  * 
00005  * Redistribution and use in source and binary forms, with or without 
00006  * modification, are permitted provided that the following conditions are 
00007  * met:
00008  * 
00009  *     * Redistributions of source code must retain the above copyright 
00010  *       notice, this list of conditions and the following disclaimer.
00011  *     * Redistributions in binary form must reproduce the above copyright 
00012  *       notice, this list of conditions and the following disclaimer in the 
00013  *       documentation and/or other materials provided with the distribution.
00014  *     * Neither the name of the Novell nor the names of its contributors 
00015  *       may be used to endorse or promote products derived from this 
00016  *       software without specific prior written permission.
00017  * 
00018  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
00019  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
00020  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
00021  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
00022  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
00023  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
00024  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
00025  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
00026  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
00027  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
00028  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00029  *--------------------------------------------------------------------------*/
00030 
00039 #include "xdasd_thread.h"
00040 
00041 #include <xdas_base.h>
00042 
00043 #include <stddef.h>
00044 
00065 thread_t thread_create(void *(*thread_proc)(void *), void * arg)
00066 {
00067 #ifdef _WIN32
00068    return (thread_t)CreateThread(0, 0, 
00069          (LPTHREAD_START_ROUTINE)thread_proc, arg, 0, 0);
00070 #else
00071    pthread_t th;
00072    return (thread_t)(pthread_create(&th, 0, thread_proc, arg)? 0: th);
00073 #endif
00074 }
00075 
00087 void * thread_wait(thread_t th)
00088 {
00089    void * result = 0;
00090 #ifdef _WIN32
00091    DWORD dwres;
00092    WaitForSingleObject((HANDLE)th, INFINITE);
00093    if (GetExitCodeThread((HANDLE)th, &dwres))
00094       result = (void *)(intptr_t)dwres;
00095    CloseHandle((HANDLE)th);
00096 #else
00097    pthread_join((pthread_t)th, &result);
00098 #endif
00099    return result;
00100 }
00101 
00102 #ifdef _WIN32
00103 
00104 /* This is the largest value we can pass as the maxCount parameter 
00105    to CreateSemaphore on any Windows platform. */
00106 #define MAX_SEM_COUNT   ((LONG)(0x7FFFFFFFL))
00107 
00118 static int cv_unblock(cv_t * cvp, int unblock_all)
00119 {
00120    int nSignalsToIssue;
00121 
00122    /* s_assert(cvp); */
00123 
00124    EnterCriticalSection(&cvp->csUnblockLock);
00125 
00126    if (cvp->waitersToUnblock != 0)
00127    {
00128       if (cvp->waitersBlocked == 0)
00129       {
00130          LeaveCriticalSection(&cvp->csUnblockLock);
00131          return 0;
00132       }
00133       if (unblock_all)
00134       {
00135          cvp->waitersToUnblock += (nSignalsToIssue = cvp->waitersBlocked);
00136          cvp->waitersBlocked = 0;
00137       }
00138       else
00139       {
00140          nSignalsToIssue = 1;
00141          cvp->waitersToUnblock++;
00142          cvp->waitersBlocked--;
00143       }
00144    }
00145    else if (cvp->waitersBlocked > cvp->waitersGone)
00146    {
00147       /* Use the non-cancellable version of sem_wait() */
00148       if (!ReleaseSemaphore(cvp->semBlockLock, 1, 0))
00149       {
00150          int err = (int)GetLastError();
00151          LeaveCriticalSection(&cvp->csUnblockLock);
00152          return err;
00153       }
00154       if (cvp->waitersGone != 0)
00155       {
00156          cvp->waitersBlocked -= cvp->waitersGone;
00157          cvp->waitersGone = 0;
00158       }
00159       if (unblock_all)
00160       {
00161          nSignalsToIssue = cvp->waitersToUnblock = cvp->waitersBlocked;
00162          cvp->waitersBlocked = 0;
00163       }
00164       else
00165       {
00166          nSignalsToIssue = cvp->waitersToUnblock = 1;
00167          cvp->waitersBlocked--;
00168       }
00169    }
00170    else
00171    {
00172       LeaveCriticalSection(&cvp->csUnblockLock);
00173       return 0;
00174    }
00175 
00176    LeaveCriticalSection(&cvp->csUnblockLock);
00177 
00178    return ReleaseSemaphore(cvp->semBlockQueue, 
00179          nSignalsToIssue, 0)? 0: (int)GetLastError();
00180 }
00181 
00190 int cv_create(cv_t * cvp)
00191 {
00192    /* s_assert(cvp); */
00193 
00194    memset(cvp, 0, sizeof(*cvp));
00195    if ((cvp->semBlockLock = CreateSemaphore(0, 1, MAX_SEM_COUNT, 0)) == 0
00196          || (cvp->semBlockQueue = CreateSemaphore(0, 0, MAX_SEM_COUNT, 0)) == 0)
00197    {
00198       if (cvp->semBlockLock)
00199          CloseHandle(cvp->semBlockLock);
00200       return 0;
00201    }
00202    InitializeCriticalSection(&cvp->csUnblockLock);
00203    return 0;
00204 }
00205 
00212 void cv_destroy(cv_t * cvp)
00213 {
00214    /* s_assert(cvpp && *cvpp); */
00215 
00216    /* Close the gate; this will synchronize this thread with
00217       all already signaled waiters to let them retract their
00218       waiter status - SEE NOTE 1 ABOVE!!! */
00219 
00220    if (WaitForSingleObject(cvp->semBlockLock, INFINITE) != WAIT_OBJECT_0)
00221       return;
00222 
00223    /* !TRY! lock csUnblockLock; try will detect busy condition
00224       and will not cause a deadlock with respect to concurrent
00225       signal/broadcast. */
00226 
00227    if (TryEnterCriticalSection(&cvp->csUnblockLock) == FALSE)
00228    {
00229       ReleaseSemaphore(cvp->semBlockLock, 1, 0);
00230       return;
00231    }
00232 
00233    /* Check whether cv is still busy (still has waiters) */
00234    if (cvp->waitersBlocked > cvp->waitersGone)
00235    {
00236       ReleaseSemaphore(cvp->semBlockLock, 1, 0);
00237       LeaveCriticalSection(&cvp->csUnblockLock);
00238       return;
00239    }
00240 
00241    /* Now it is safe to destroy */
00242    CloseHandle(cvp->semBlockLock);
00243    CloseHandle(cvp->semBlockQueue);
00244    LeaveCriticalSection(&cvp->csUnblockLock);
00245    DeleteCriticalSection(&cvp->csUnblockLock);
00246 }
00247 
00257 int cv_wait(cv_t * cvp, mutex_t * mp)
00258 {
00259    DWORD rv;
00260    int err = 0;
00261    int nSignalsWasLeft;
00262 
00263    /* s_assert(cvp && mp); */
00264 
00265    if (WaitForSingleObject(cvp->semBlockLock, INFINITE) != WAIT_OBJECT_0)
00266       return -1;
00267 
00268    ++cvp->waitersBlocked;
00269    if (!ReleaseSemaphore(cvp->semBlockLock, 1, 0))
00270       return -1;
00271 
00272    /* Now we can release 'mutex' and... */
00273    LeaveCriticalSection(mp);
00274 
00275    /* ...wait to be awakened by
00276                  pthread_cond_signal, or
00277                  pthread_cond_broadcast, or
00278                  timeout, or
00279                  thread cancellation
00280    
00281       Note:
00282            sem_timedwait is a cancellation point,
00283            hence providing the mechanism for making
00284            pthread_cond_wait a cancellation point.
00285            We use the cleanup mechanism to ensure we
00286            re-lock the mutex and adjust (to)unblock(ed) waiters
00287            counts if we are cancelled, timed out or signalled.
00288     */
00289 
00290    if ((rv = WaitForSingleObject(cvp->semBlockQueue, INFINITE)) != WAIT_OBJECT_0)
00291       err = rv == WAIT_TIMEOUT? -1: -2;
00292 
00293    /* Whether we got here as a result of signal/broadcast or because of
00294       timeout on wait or thread cancellation we indicate that we are no
00295       longer waiting. The waiter is responsible for adjusting waiters
00296       (to)unblock(ed) counts (protected by unblock lock). */
00297 
00298    EnterCriticalSection(&cvp->csUnblockLock);
00299 
00300    if ((nSignalsWasLeft = cvp->waitersToUnblock) != 0)
00301       --cvp->waitersToUnblock;
00302    else if (++cvp->waitersGone == (unsigned)(-1) / 2)
00303    {
00304       /* Use the non-cancellable version of sem_wait() */
00305       if (WaitForSingleObject(cvp->semBlockLock, INFINITE) != WAIT_OBJECT_0)
00306          return (int)GetLastError();
00307 
00308       cvp->waitersBlocked -= cvp->waitersGone;
00309       if (!ReleaseSemaphore(cvp->semBlockLock, 1, 0))
00310          return (int)GetLastError();
00311 
00312       cvp->waitersGone = 0;
00313    }
00314    LeaveCriticalSection(&cvp->csUnblockLock);
00315    if (nSignalsWasLeft == 1)
00316       if (!ReleaseSemaphore(cvp->semBlockLock, 1, 0))
00317          return (int)GetLastError();
00318 
00319    /* XSH: Upon successful return, the mutex has been locked and is owned
00320       by the calling thread */
00321    EnterCriticalSection(mp);
00322    return err;
00323 }
00324 
00333 int cv_signal(cv_t * cvp)
00334 {
00335    /* s_assert(cvp); */
00336    return cv_unblock(cvp, 0);
00337 }
00338 
00347 int cv_broadcast(cv_t * cvp)
00348 {
00349    /* s_assert(cvp); */
00350    return cv_unblock(cvp, 1);
00351 }
00352 
00353 #else
00354 
00362 static void p_operation_cleanup(void * arg)
00363       { pthread_mutex_unlock(&((sem_t *)arg)->lock); }
00364 
00374 int sem_create(sem_t * sp, int count)
00375 {
00376    if (pthread_mutex_init(&sp->lock, 0) != 0)
00377       return -1;
00378    if (pthread_cond_init(&sp->cond, 0) != 0)
00379    {
00380       pthread_mutex_destroy(&sp->lock);
00381       return -1;
00382    }
00383    sp->count = count;
00384    return 0;
00385 }
00386 
00393 void sem_destroy(sem_t * sp)
00394 {
00395    (void)pthread_mutex_destroy(&sp->lock);
00396    (void)pthread_cond_destroy(&sp->cond);
00397 }
00398 
00405 void sem_wait(sem_t * sp)
00406 {
00407    pthread_mutex_lock(&sp->lock);
00408    pthread_cleanup_push(p_operation_cleanup, sp);
00409 
00410    while (sp->count <= 0)
00411       pthread_cond_wait(&sp->cond, &sp->lock);
00412    sp->count--;
00413 
00414    pthread_cleanup_pop(1);
00415 }
00416 
00424 void sem_signal(sem_t * sp, int count)
00425 {
00426    pthread_mutex_lock(&sp->lock);
00427 
00428    sp->count += count;
00429    if (sp->count <= 0)
00430       pthread_cond_signal(&sp->cond);
00431 
00432    pthread_mutex_unlock(&sp->lock);
00433 }
00434 
00435 #endif /* ?_WIN32 */
00436 

Generated on Thu Aug 20 22:33:06 2009 for OpenXDAS by  doxygen 1.5.6