windows implementation needs more testing
authorwohl <wohl@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 19 May 2003 16:52:18 +0000 (16:52 +0000)
committerwohl <wohl@cb58f699-b61c-0410-a6fe-9272a202ed29>
Mon, 19 May 2003 16:52:18 +0000 (16:52 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-sp/trunk@460 cb58f699-b61c-0410-a6fe-9272a202ed29

shib/shib-threads-win32.cpp [new file with mode: 0644]

diff --git a/shib/shib-threads-win32.cpp b/shib/shib-threads-win32.cpp
new file mode 100644 (file)
index 0000000..8f2ea52
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * shib-threads-win32.cpp -- an abstraction around win32 threads
+ *
+ * Created by: aaron wohl <xshib@awohl.com>
+ *
+ * $Id$
+ */
+
+#include "internal.h"
+#include "shib-threads.h"
+
+#ifndef WIN32
+#error "This implementaiton is just for windows 32"
+#endif
+
+#include <stdexcept>
+
+using namespace std;
+using namespace shibboleth;
+
+// base error code for a routine to return onf failure
+#define THREAD_ERROR (1)
+#define THREAD_ERROR_TIMEOUT (2)
+
+static void note_last_error(int rc) {
+    // set a breakpoint here to see windows error codes for failing
+    // thread operations
+}
+
+// windows returns non zero for sucess pthreads returns zero
+static int map_windows_error_status_to_pthreads(int rc) {
+  if(rc!=0)  // sucess?
+    return 0; // yes
+  int last_error=GetLastError();
+  note_last_error(last_error);
+  return THREAD_ERROR;
+}
+
+// win32 implementation of the Shib Target Threads API
+
+//
+// "Private" Implementation
+//
+
+// two levels of classes are needed here
+// in case InitializeCriticalSection
+// throws an exception we can keep from
+// calling the critical_section destructor
+// on unitilized data, or it could be done with a flag
+class critical_section_data {
+public:
+  CRITICAL_SECTION cs;
+  critical_section_data(){
+    InitializeCriticalSection(&cs);    
+  }
+};
+
+class critical_section {
+private:
+  critical_section_data        cse;
+public:
+  critical_section(){}
+  ~critical_section(){
+    DeleteCriticalSection (&cse.cs);
+  }
+  void enter(void) {
+    EnterCriticalSection(&cse.cs);
+  }
+  void leave(void) {
+    LeaveCriticalSection(&cse.cs);
+  }
+};
+
+// hold a critical section over the lifetime of this object
+// used to make a stack variable that unlocks automaticly
+// on return/throw
+class with_crit_section {
+private:
+  critical_section& cs;
+public:
+  with_crit_section(critical_section& acs):cs(acs){
+    cs.enter();
+  }
+  ~with_crit_section(){
+    cs.leave();
+  }
+};
+
+class ThreadImpl : public Thread {
+private:
+  HANDLE thread_id;
+public:
+  ThreadImpl(void* (*start_routine)(void*), void* arg):thread_id(0){
+        thread_id=CreateThread(
+      0, // security attributes
+      0, // use default stack size, maybe this should be setable
+     (LPTHREAD_START_ROUTINE ) start_routine,
+     arg,
+     0, // flags, default is ignore stacksize and dont create suspeneded which
+        // is what we want
+     0);
+  if(thread_id!=0) {
+      int rc=map_windows_error_status_to_pthreads(0);
+         throw("thread create failed");
+  }
+  }
+
+  int detach() {
+    if(thread_id==0)
+      return THREAD_ERROR;
+    int rc=map_windows_error_status_to_pthreads(CloseHandle(thread_id));
+    thread_id=0;
+    return rc;
+  }
+  ~ThreadImpl() {
+    (void)detach();
+  }
+
+  int join(void** thread_return) {
+  if(thread_id==0)
+      return THREAD_ERROR;
+  if(thread_return!=0)
+    *thread_return=0;
+  int rc=WaitForSingleObject(thread_id,INFINITE);
+  switch(rc) {
+  case WAIT_OBJECT_0:
+         map_windows_error_status_to_pthreads(
+                 GetExitCodeThread(thread_id,(unsigned long *)thread_return));
+  default:
+    return THREAD_ERROR+1;
+  }
+  }
+  
+  int kill(int signo) {
+    if(thread_id==0)
+      return THREAD_ERROR;
+    return map_windows_error_status_to_pthreads(TerminateThread(thread_id,signo));
+  }
+
+};
+
+class MutexImpl : public Mutex {
+private:
+  HANDLE mhandle;
+public:
+  MutexImpl():mhandle(0){
+    mhandle=CreateMutex(0,false,0);
+    if(mhandle==0)
+      throw("CreateMutex for failed");
+  }
+  ~MutexImpl(){
+    if((mhandle!=0)&&(!CloseHandle(mhandle))) 
+      throw("CloseHandle for CondWaitImpl failed");
+  }
+  int lock2_timed(HANDLE lock_me_too,int timeout) {
+#define NUM_HANDLES (2)
+   HANDLE hs[NUM_HANDLES];
+   hs[0]=mhandle;
+   hs[1]=lock_me_too;
+   int rc=WaitForMultipleObjects(NUM_HANDLES,hs,true,timeout);
+   if((rc>=WAIT_OBJECT_0)&&
+      (rc<=(WAIT_OBJECT_0+NUM_HANDLES-1)))
+     return 0;
+   if(rc==WAIT_TIMEOUT)
+      return THREAD_ERROR_TIMEOUT;
+    return map_windows_error_status_to_pthreads(rc);
+  }
+  int lock() {
+   map_windows_error_status_to_pthreads(WaitForSingleObject(mhandle,0));
+  }
+  int unlock() {
+    map_windows_error_status_to_pthreads(ReleaseMutex(mhandle));
+  }
+};
+
+class CondWaitImpl : public CondWait {
+private:
+  HANDLE cond;
+
+public:
+  CondWaitImpl():cond(CreateEvent(0,false,false,0)){
+    if(cond==0)
+      throw("CreateEvent for CondWaitImpl failed");
+  };
+  int timedwait(Mutex* mutex, int delay_seconds)
+                 { return timedwait (dynamic_cast<MutexImpl*>(mutex), delay_seconds); }
+  ~CondWaitImpl() {
+    if((cond!=0)&&(!CloseHandle(cond))) 
+      throw("CloseHandle for CondWaitImpl failed");
+   }
+
+  int wait(Mutex* mutex) { return wait (dynamic_cast<MutexImpl*>(mutex)); }
+
+  int signal() {
+    if(!SetEvent(cond))
+     return map_windows_error_status_to_pthreads(0);
+    return 0;
+  }
+  int broadcast() {
+    throw("CondWaitImpl not implemented on win32");
+  }
+
+  // wait for myself to signal and this mutex or the timeout
+  int lock2(MutexImpl *mutex,int delay_ms) {
+    int rc=mutex->unlock();
+    if(rc!=0)
+      return rc;
+    rc=mutex->lock2_timed(cond,delay_ms);
+    switch(rc) {
+    case 0:
+      return 0;
+    case THREAD_ERROR_TIMEOUT:
+      // timeout return with the mutex locked
+      return mutex->lock();        
+    default:
+      return rc;
+    }
+    return 0;
+  }
+
+  int timedwait(MutexImpl* mutex, int delay_seconds) {
+    return lock2(mutex,delay_seconds);
+  }
+};
+
+class RWLockImpl : public RWLock {
+private:
+  // used to protect read or write to the data below
+  critical_section cs;
+  // event handle threads wait on when the lock they want is busy
+  // normaly set to signaled all the time, if some thread cant get what
+  // they want they reset it and sleep.  on releasing a lock set it to
+  // signaled if someone may have wanted what you just released
+  HANDLE wake_waiters;
+  // number of threads holding a read lock
+  int num_readers;
+  // true iff there a writer has our lock
+  bool have_writer;
+
+public:
+  RWLockImpl():wake_waiters(0),num_readers(0),have_writer(true) {
+    with_crit_section acs(cs);
+    wake_waiters=CreateEvent(0,true,true,0);
+    have_writer=false;
+    if(wake_waiters==0)
+      throw("CreateEvent for RWLockImpl failed");
+  }
+  ~RWLockImpl() { 
+     with_crit_section acs(cs);
+     if((wake_waiters!=0)&&(!CloseHandle(wake_waiters))) 
+       throw("CloseHandle for RWLockImpl failed");
+     wake_waiters=0;
+   }
+
+  int rdlock() {
+    while(1) {
+     // wait for the lock maybe being availible
+     // we will find out for sure inside the critical section
+     if(WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0) 
+       return map_windows_error_status_to_pthreads(0);
+     {
+       with_crit_section alock(cs);
+      // invariant not locked for reading and writing
+       if((num_readers!=0)&&(have_writer))
+              return THREAD_ERROR;
+       // if no writer we can join any existing readers
+       if(!have_writer) {
+         num_readers++;
+        return 0;
+       }
+       // have a writer, mark the syncronization object
+       // so everyone waits, when the writer unlocks it will wake us
+       if(!ResetEvent(wake_waiters))
+         return map_windows_error_status_to_pthreads(0);
+     }
+    }
+    return THREAD_ERROR+2;
+  }
+
+  int wrlock() {
+    while(1) {
+    // wait for the lock maybe being availible
+    // we will find out for sure inside the critical section
+    if(WaitForSingleObject(wake_waiters,INFINITE)!=WAIT_OBJECT_0) 
+      return map_windows_error_status_to_pthreads(0);
+    {
+     with_crit_section bla(cs);
+    // invariant not locked for reading and writing
+     if((num_readers!=0)&&(have_writer))
+            return THREAD_ERROR;
+     // if no writer and no readers we can become the writer
+    if((num_readers==0)&&(!have_writer)) {
+      have_writer=true;
+      return 0;
+     }
+     // lock is busy, the unlocker will wake us
+     if(!ResetEvent(wake_waiters))
+       return map_windows_error_status_to_pthreads(0);
+    }
+    }
+    return THREAD_ERROR+2;
+  }
+
+  int unlock() {
+    with_crit_section mumble(cs);
+    // invariant not locked for reading and writing
+    if((num_readers!=0)&&(have_writer))
+      return THREAD_ERROR;
+    // error if nothing locked
+    if((num_readers==0)&&(!have_writer))
+      return THREAD_ERROR+1;
+    // if there was a writer it has to be us so unlock write lock 
+    have_writer=false;
+    // if there where any reades there is one less now
+    if(num_readers>0)
+      num_readers--;
+    // if no readers left wake up any readers/writers waiting
+    // to have a go at it
+    if(num_readers==0)
+      if(!SetEvent(wake_waiters))
+         return map_windows_error_status_to_pthreads(0);
+    return 0;
+  }
+};
+
+static __declspec( thread ) void *thread_data;
+typedef void (*destroy_hook_type)(void*);
+
+class ThreadKeyImpl : public ThreadKey {
+private:
+  destroy_hook_type destroy_hook;
+
+public:
+  ThreadKeyImpl(void (*destroy_fcn)(void*)):destroy_hook(destroy_fcn){};
+  ~ThreadKeyImpl() { destroy_hook(thread_data); }
+
+  int setData(void* data) { thread_data=data; }
+  void* getData() { return thread_data; }
+  };
+