windows-pthread-rwlock-20081017
[openafs.git] / src / WINNT / pthread / pthread.c
index 33a17a4..739c7b9 100644 (file)
@@ -33,6 +33,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <process.h>
 #include <errno.h>
 #include <sys/timeb.h>
 
@@ -126,7 +127,7 @@ int pthread_mutex_trylock(pthread_mutex_t *mp) {
            if (mp->isLocked) {
                /* same thread tried to recursively lock, fail */
                LeaveCriticalSection(&mp->cs);
-               rc = EBUSY;
+               rc = EDEADLK;
            } else {
                mp->isLocked = 1;
                mp->tid = GetCurrentThreadId();
@@ -161,8 +162,14 @@ int pthread_mutex_lock(pthread_mutex_t *mp) {
             */
            LeaveCriticalSection(&mp->cs);
            rc = EDEADLK;
+#ifdef PTHREAD_DEBUG
+            DebugBreak();
+#endif
        }
     } else {
+#ifdef PTHREAD_DEBUG
+        DebugBreak();
+#endif
        rc = EINVAL;
     }
        
@@ -178,9 +185,15 @@ int pthread_mutex_unlock(pthread_mutex_t *mp) {
            mp->tid = 0;
            LeaveCriticalSection(&mp->cs);
        } else {
-           rc = 0;
+#ifdef PTHREAD_DEBUG
+            DebugBreak();
+#endif
+           rc = EPERM;
        }
     } else {
+#ifdef PTHREAD_DEBUG
+        DebugBreak();
+#endif
        rc = EINVAL;
     }
     return rc;
@@ -192,12 +205,210 @@ int pthread_mutex_destroy(pthread_mutex_t *mp) {
     if (mp != NULL) {
        DeleteCriticalSection(&mp->cs);
     } else {
+#ifdef PTHREAD_DEBUG
+        DebugBreak();
+#endif
        rc = EINVAL;
     }
 
     return rc;
 }
 
+int pthread_rwlock_destroy(pthread_rwlock_t *rwp)
+{
+    int rc = 0;
+
+    if (rwp != NULL) {
+        pthread_mutex_destroy(&rwp->read_access_completion_mutex);
+        pthread_mutex_destroy(&rwp->write_access_mutex);
+        pthread_cond_destroy(&rwp->read_access_completion_wait);
+    } else {
+#ifdef PTHREAD_DEBUG
+        DebugBreak();
+#endif
+       rc = EINVAL;
+    }
+
+    return rc;
+}
+
+int pthread_rwlock_init(pthread_rwlock_t *rwp, const pthread_rwlockattr_t *attr)
+{
+    int rc = 0;
+
+    if (rwp == NULL)
+        return EINVAL;
+
+    rwp->readers = 0;
+
+    rc = pthread_mutex_init(&rwp->write_access_mutex, NULL);
+    if (rc)
+        return rc;
+
+    rc = pthread_mutex_init(&rwp->read_access_completion_mutex, NULL);
+    if (rc)
+        goto error1;
+
+    rc = pthread_cond_init(&rwp->read_access_completion_wait, NULL);
+    if (rc == 0)
+        return 0;       /* success */
+
+    pthread_mutex_destroy(&rwp->read_access_completion_mutex);
+
+  error1:
+    pthread_mutex_destroy(&rwp->write_access_mutex);
+
+    return rc;
+}
+
+int pthread_rwlock_wrlock(pthread_rwlock_t *rwp)
+{
+    int rc = 0;
+
+    if (rwp == NULL)
+        return EINVAL;
+
+    if ((rc = pthread_mutex_lock(&rwp->write_access_mutex)) != 0)
+        return rc;
+
+    if ((rc = pthread_mutex_lock(&rwp->read_access_completion_mutex)) != 0)
+    {
+        pthread_mutex_unlock(&rwp->write_access_mutex);
+        return rc;
+    }
+
+    while (rc == 0 && rwp->readers > 0) {
+        rc = pthread_cond_wait( &rwp->read_access_completion_wait, 
+                                &rwp->read_access_completion_mutex);
+    }
+
+    pthread_mutex_unlock(&rwp->read_access_completion_mutex);
+
+    if (rc)
+        pthread_mutex_unlock(&rwp->write_access_mutex);
+
+    return rc;
+}
+
+int pthread_rwlock_rdlock(pthread_rwlock_t *rwp)
+{
+    int rc = 0;
+
+    if (rwp == NULL)
+        return EINVAL;
+
+    if ((rc = pthread_mutex_lock(&rwp->write_access_mutex)) != 0)
+        return rc;
+
+    if ((rc = pthread_mutex_lock(&rwp->read_access_completion_mutex)) != 0)
+    {
+        pthread_mutex_unlock(&rwp->write_access_mutex);
+        return rc;
+    }
+
+    rwp->readers++;
+
+    pthread_mutex_unlock(&rwp->read_access_completion_mutex);
+
+    pthread_mutex_unlock(&rwp->write_access_mutex);
+
+    return rc;
+        
+}
+
+int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwp)
+{
+    int rc = 0;
+
+    if (rwp == NULL)
+        return EINVAL;
+
+    if ((rc = pthread_mutex_trylock(&rwp->write_access_mutex)) != 0)
+        return rc;
+
+    if ((rc = pthread_mutex_trylock(&rwp->read_access_completion_mutex)) != 0) {
+        pthread_mutex_unlock(&rwp->write_access_mutex);
+        return rc;
+    }
+
+    rwp->readers++;
+
+    pthread_mutex_unlock(&rwp->read_access_completion_mutex);
+
+    pthread_mutex_unlock(&rwp->write_access_mutex);
+
+    return rc;
+}
+
+int pthread_rwlock_trywrlock(pthread_rwlock_t *rwp)
+{
+    int rc = 0;
+
+    if (rwp == NULL)
+        return EINVAL;
+
+    if ((rc = pthread_mutex_trylock(&rwp->write_access_mutex)) != 0)
+        return rc;
+
+    if ((rc = pthread_mutex_trylock(&rwp->read_access_completion_mutex)) != 0)
+    {
+        pthread_mutex_unlock(&rwp->write_access_mutex);
+        return rc;
+    }
+
+    if (rwp->readers > 0)
+        rc = EBUSY;
+
+    pthread_mutex_unlock(&rwp->read_access_completion_mutex);
+
+    if (rc)
+        pthread_mutex_unlock(&rwp->write_access_mutex);
+
+    return rc;
+}
+
+int pthread_rwlock_unlock(pthread_rwlock_t *rwp)
+{
+    int rc = 0;
+
+    if (rwp == NULL)
+        return EINVAL;
+
+    rc = pthread_mutex_trylock(&rwp->write_access_mutex);
+    if (rc != EDEADLK)
+    {
+        /* unlock a read lock */
+        if (rc == 0)
+            pthread_mutex_unlock(&rwp->write_access_mutex);
+        
+        if ((rc = pthread_mutex_lock(&rwp->read_access_completion_mutex)) != 0)
+        {
+            pthread_mutex_unlock(&rwp->write_access_mutex);
+            return rc;
+        }
+
+        if (rwp->readers <= 0)
+        {
+            rc = EINVAL;
+        }
+        else 
+        {
+            if (--rwp->readers == 0) 
+                pthread_cond_broadcast(&rwp->read_access_completion_wait);
+        }
+
+        pthread_mutex_unlock(&rwp->read_access_completion_mutex);
+    } 
+    else
+    {
+        /* unlock a write lock */
+        rc = pthread_mutex_unlock(&rwp->write_access_mutex);
+    }
+
+    return rc;
+}
+
+
 /*
  * keys is used to keep track of which keys are currently
  * in use by the threads library.  pthread_tsd_mutex is used
@@ -260,6 +471,24 @@ static void create_once(void) {
     pthread_cache_done = 1;
 }
 
+static void cleanup_pthread_cache(void) {
+    thread_p cur = NULL, next = NULL;
+
+    if (pthread_cache_done) {
+       for(queue_Scan(&active_Q, cur, next, thread)) {
+           queue_Remove(cur);
+       }
+       for(queue_Scan(&cache_Q, cur, next, thread)) {
+           queue_Remove(cur);
+       }
+
+       pthread_mutex_destroy(&active_Q_mutex);
+       pthread_mutex_destroy(&cache_Q_mutex);
+
+       pthread_cache_done = 0;
+    }
+}      
+
 static void put_thread(thread_p old) {
  
     CloseHandle(old->t_handle);
@@ -360,6 +589,23 @@ static void tsd_free_all(char *tsd[PTHREAD_KEYS_MAX]) {
     } while(call_more_destructors);
 }
 
+static void cleanup_global_tsd(void)
+{
+    thread_p cur = NULL, next = NULL;
+
+    if (tsd_done) {
+       for(queue_Scan(&active_Q, cur, next, thread)) {
+           tsd_free_all(cur->tsd);
+       }
+
+       TlsFree(tsd_pthread_index);
+       tsd_pthread_index = 0xFFFFFFFF;
+       TlsFree(tsd_index);
+       tsd_index = 0xFFFFFFFF;
+       tsd_done = 0;
+    }
+}
+
 static DWORD WINAPI afs_pthread_create_stub(LPVOID param) {
     pthread_create_t *t = (pthread_create_t *) param;
     void *rc;
@@ -433,7 +679,7 @@ static size_t terminate_thread_wakeup_list_size = 0;
 
 static DWORD WINAPI terminate_thread_routine(LPVOID param) {
     thread_p cur, next;
-    size_t native_thread_count;
+    DWORD native_thread_count;
     int should_terminate;
     int terminate_thread_wakeup_list_index;
 
@@ -700,10 +946,27 @@ static pthread_once_t waiter_cache_once = PTHREAD_ONCE_INIT;
  
 static void init_waiter_cache(void) {
     InitializeCriticalSection(&waiter_cache_cs);
-    waiter_cache_init = 1;
     queue_Init(&waiter_cache);
+    waiter_cache_init = 1;
 }
  
+static void cleanup_waiter_cache(void)
+{
+    cond_waiters_t * cur = NULL, * next = NULL;
+
+    if (waiter_cache_init) {
+       for(queue_Scan(&waiter_cache, cur, next, cond_waiter)) {
+           queue_Remove(cur);
+
+           CloseHandle(cur->event);
+           free(cur);
+       }
+
+       DeleteCriticalSection(&waiter_cache_cs);
+       waiter_cache_init = 0;
+    }
+}
+
 static cond_waiters_t *get_waiter() {
     cond_waiters_t *new = NULL;
  
@@ -754,66 +1017,63 @@ static int cond_wait_internal(pthread_cond_t *cond, pthread_mutex_t *mutex, cons
        queue_Append(&cond->waiting_threads, my_entry);
        LeaveCriticalSection(&cond->cs);
 
-       if (!pthread_mutex_unlock(mutex)) {
+       if (pthread_mutex_unlock(mutex) == 0) {
            switch(WaitForSingleObject(my_entry->event, time)) {
-               case WAIT_FAILED:
-                   rc = -1;
-                   break;
-               case WAIT_TIMEOUT:
-                   rc = ETIME;
-                   /*
-                    * This is a royal pain.  We've timed out waiting
-                    * for the signal, but between the time out and here
-                    * it is possible that we were actually signalled by 
-                    * another thread.  So we grab the condition lock
-                    * and scan the waiting thread queue to see if we are
-                    * still there.  If we are, we just remove ourselves.
-                    *
-                    * If we are no longer listed in the waiter queue,
-                    * it means that we were signalled after the time
-                    * out occurred and so we have to do another wait
-                    * WHICH HAS TO SUCCEED!  In this case, we reset
-                    * rc to indicate that we were signalled.
-                    *
-                    * We have to wait or otherwise, the event
-                    * would be cached in the signalled state, which
-                    * is wrong.  It might be more efficient to just
-                    * close and reopen the event.
-                    */
-                   EnterCriticalSection(&cond->cs);
-                   for(queue_Scan(&cond->waiting_threads, cur,
-                                  next, cond_waiter)) {
-                       if (cur == my_entry) {
-                           hasnt_been_signalled = 1;
-                           break;
-                       }
-                   }
-                   if (hasnt_been_signalled) {
-                       queue_Remove(cur);
-                   } else {
-                       rc = 0;
-                       if (ResetEvent(my_entry->event)) {
-                           if (pthread_mutex_lock(mutex)) {
-                               rc = -5;
-                           }
-                       } else {
-                           rc = -6;
-                       }
-                   }
-                   LeaveCriticalSection(&cond->cs);
-                   break;
-               case WAIT_ABANDONED:
-                   rc = -2;
-                   break;
-               case WAIT_OBJECT_0:
-                   if (pthread_mutex_lock(mutex)) {
-                       rc = -3;
-                   }
-                   break;
-               default:
-                   rc = -4;
-                   break;
-           }
+            case WAIT_FAILED:
+                rc = -1;
+                break;
+            case WAIT_TIMEOUT:
+                rc = ETIMEDOUT;
+                /*
+                 * This is a royal pain.  We've timed out waiting
+                 * for the signal, but between the time out and here
+                 * it is possible that we were actually signalled by 
+                 * another thread.  So we grab the condition lock
+                 * and scan the waiting thread queue to see if we are
+                 * still there.  If we are, we just remove ourselves.
+                 *
+                 * If we are no longer listed in the waiter queue,
+                 * it means that we were signalled after the time
+                 * out occurred and so we have to do another wait
+                 * WHICH HAS TO SUCCEED!  In this case, we reset
+                 * rc to indicate that we were signalled.
+                 *
+                 * We have to wait or otherwise, the event
+                 * would be cached in the signalled state, which
+                 * is wrong.  It might be more efficient to just
+                 * close and reopen the event.
+                 */
+                EnterCriticalSection(&cond->cs);
+                for(queue_Scan(&cond->waiting_threads, cur,
+                                next, cond_waiter)) {
+                    if (cur == my_entry) {
+                        hasnt_been_signalled = 1;
+                        break;
+                    }
+                }
+                if (hasnt_been_signalled) {
+                    queue_Remove(cur);
+                } else {
+                    rc = 0;
+                    if (!ResetEvent(my_entry->event)) {
+                        rc = -6;
+                    }
+                }
+                LeaveCriticalSection(&cond->cs);
+                break;
+            case WAIT_ABANDONED:
+                rc = -2;
+                break;
+            case WAIT_OBJECT_0:
+                rc = 0;
+                break;
+            default:
+                rc = -4;
+                break;
+            }
+            if (pthread_mutex_lock(mutex) != 0) {
+                rc = -3;
+            }   
        } else {
            rc = EINVAL;
        }
@@ -838,7 +1098,7 @@ int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) {
 int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) {
     int rc = 0;
     struct _timeb now, then;
-    short n_milli, t_milli;
+    afs_uint32 n_milli, t_milli;
 
     if (abstime->tv_nsec < 1000000000) {
 
@@ -873,7 +1133,7 @@ int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const s
                 * round up the wait time here.
                 */
                rc = cond_wait_internal(cond, mutex, 
-                                ((then.time * 1000) + (t_milli)));
+                                (DWORD)((then.time * 1000) + (t_milli)));
            } else {
                rc = EINVAL;
            }
@@ -1043,6 +1303,9 @@ void *pthread_getspecific(pthread_key_t key) {
     void *rc = NULL;
     char **tsd = TlsGetValue(tsd_index);
 
+    if (tsd == NULL)
+        return NULL;
+
     if ((key > -1) && (key < PTHREAD_KEYS_MAX )) {
        rc = (void *) *(tsd + key);
     }
@@ -1111,6 +1374,9 @@ int pthread_setspecific(pthread_key_t key, const void *value) {
     int rc = 0;
     char **tsd;
 
+    /* make sure all thread-local storage has been allocated */
+    pthread_self();
+
     if (p_tsd_done || (!pthread_once(&pthread_tsd_once, pthread_tsd_init))) {
        if ((key > -1) && (key < PTHREAD_KEYS_MAX )) {
            if (!pthread_mutex_lock(&pthread_tsd_mutex)) {
@@ -1251,3 +1517,37 @@ void pthread_exit(void *status) {
     RaiseException(PTHREAD_EXIT_EXCEPTION, 0, 0, NULL);
 
 }
+
+/*
+ * DllMain() -- Entry-point function called by the DllMainCRTStartup()
+ *     function in the MSVC runtime DLL (msvcrt.dll).
+ *
+ *     Note: the system serializes calls to this function.
+ */
+BOOL WINAPI
+DllMain(HINSTANCE dllInstHandle,/* instance handle for this DLL module */
+        DWORD reason,           /* reason function is being called */
+        LPVOID reserved)
+{                               /* reserved for future use */
+    switch (reason) {
+    case DLL_PROCESS_ATTACH:
+        /* library is being attached to a process */
+        /* disable thread attach/detach notifications */
+        (void)DisableThreadLibraryCalls(dllInstHandle);
+
+       pthread_once(&pthread_cache_once, create_once);
+       pthread_once(&global_tsd_once, tsd_once);
+       pthread_once(&waiter_cache_once, init_waiter_cache);
+       return TRUE;
+
+    case DLL_PROCESS_DETACH:
+       cleanup_waiter_cache();
+       cleanup_global_tsd();
+       cleanup_pthread_cache();
+        return TRUE;
+
+    default:
+        return FALSE;
+    }
+}
+