First shot at thread-safety.
authorRyan C. Gordon <icculus@icculus.org>
Sat, 30 Mar 2002 16:44:09 +0000
changeset 145 d6385584f6c4
parent 144 819455c581e5
child 146 93dba4d3d0d2
First shot at thread-safety.
physfs.c
physfs_internal.h
platform/unix.c
--- a/physfs.c	Sat Mar 30 01:57:53 2002 +0000
+++ b/physfs.c	Sat Mar 30 16:44:09 2002 +0000
@@ -85,7 +85,6 @@
 
 
 /* General PhysicsFS state ... */
-
 static int initialized = 0;
 static ErrMsg *errorMessages = NULL;
 static DirInfo *searchPath = NULL;
@@ -96,6 +95,9 @@
 static char *userDir = NULL;
 static int allowSymLinks = 0;
 
+/* mutexes ... */
+static void *errorLock = NULL;     /* protects error message list.        */
+static void *stateLock = NULL;     /* protects other PhysFS static state. */
 
 
 /* functions ... */
@@ -105,6 +107,7 @@
     ErrMsg *i;
     int tid;
 
+    __PHYSFS_platformGrabMutex(errorLock);
     if (errorMessages != NULL)
     {
         tid = __PHYSFS_platformGetThreadID();
@@ -112,9 +115,13 @@
         for (i = errorMessages; i != NULL; i = i->next)
         {
             if (i->tid == tid)
+            {
+                __PHYSFS_platformReleaseMutex(errorLock);
                 return(i);
+            } /* if */
         } /* for */
     } /* if */
+    __PHYSFS_platformReleaseMutex(errorLock);
 
     return(NULL);   /* no error available. */
 } /* findErrorForCurrentThread */
@@ -137,8 +144,11 @@
 
         memset((void *) err, '\0', sizeof (ErrMsg));
         err->tid = __PHYSFS_platformGetThreadID();
+
+        __PHYSFS_platformGrabMutex(errorLock);
         err->next = errorMessages;
         errorMessages = err;
+        __PHYSFS_platformReleaseMutex(errorLock);
     } /* if */
 
     err->errorAvailable = 1;
@@ -147,19 +157,6 @@
 } /* __PHYSFS_setError */
 
 
-static void freeErrorMessages(void)
-{
-    ErrMsg *i;
-    ErrMsg *next;
-
-    for (i = errorMessages; i != NULL; i = next)
-    {
-        next = i->next;
-        free(i);
-    } /* for */
-} /* freeErrorMessages */
-
-
 const char *PHYSFS_getLastError(void)
 {
     ErrMsg *err = findErrorForCurrentThread();
@@ -172,6 +169,20 @@
 } /* PHYSFS_getLastError */
 
 
+/* MAKE SURE that errorLock is held before calling this! */
+static void freeErrorMessages(void)
+{
+    ErrMsg *i;
+    ErrMsg *next;
+
+    for (i = errorMessages; i != NULL; i = next)
+    {
+        next = i->next;
+        free(i);
+    } /* for */
+} /* freeErrorMessages */
+
+
 void PHYSFS_getLinkedVersion(PHYSFS_Version *ver)
 {
     if (ver != NULL)
@@ -212,16 +223,17 @@
 
     di = (DirInfo *) malloc(sizeof (DirInfo));
     if (di == NULL)
+    {
         dirHandle->funcs->dirClose(dirHandle);
-    BAIL_IF_MACRO(di == NULL, ERR_OUT_OF_MEMORY, 0);
+        BAIL_IF_MACRO(di == NULL, ERR_OUT_OF_MEMORY, 0);
+    } /* if */
 
     di->dirName = (char *) malloc(strlen(newDir) + 1);
     if (di->dirName == NULL)
     {
         free(di);
         dirHandle->funcs->dirClose(dirHandle);
-        __PHYSFS_setError(ERR_OUT_OF_MEMORY);
-        return(0);
+        BAIL_MACRO(ERR_OUT_OF_MEMORY, 0);
     } /* if */
 
     di->next = NULL;
@@ -231,6 +243,7 @@
 } /* buildDirInfo */
 
 
+/* MAKE SURE you've got the stateLock held before calling this! */
 static int freeDirInfo(DirInfo *di, FileHandleList *openList)
 {
     FileHandleList *i;
@@ -243,7 +256,7 @@
         const DirHandle *h = ((FileHandle *) &(i->handle.opaque))->dirHandle;
         BAIL_IF_MACRO(h == di->dirHandle, ERR_FILES_STILL_OPEN, 0);
     } /* for */
-
+    
     di->dirHandle->funcs->dirClose(di->dirHandle);
     free(di->dirName);
     free(di);
@@ -356,6 +369,30 @@
 } /* calculateBaseDir */
 
 
+static int initializeMutexes(void)
+{
+    errorLock = __PHYSFS_platformCreateMutex();
+    if (errorLock == NULL)
+        goto initializeMutexes_failed;
+
+    stateLock = __PHYSFS_platformCreateMutex();
+    if (stateLock == NULL)
+        goto initializeMutexes_failed;
+
+    return(1);  /* success. */
+
+initializeMutexes_failed:
+    if (errorLock != NULL)
+        __PHYSFS_platformDestroyMutex(errorLock);
+
+    if (stateLock != NULL)
+        __PHYSFS_platformDestroyMutex(stateLock);
+
+    errorLock = stateLock = NULL;
+    return(0);  /* failed. */
+} /* initializeMutexes */
+
+
 int PHYSFS_init(const char *argv0)
 {
     char *ptr;
@@ -364,6 +401,8 @@
     BAIL_IF_MACRO(argv0 == NULL, ERR_INVALID_ARGUMENT, 0);
     BAIL_IF_MACRO(!__PHYSFS_platformInit(), NULL, 0);
 
+    BAIL_IF_MACRO(!initializeMutexes(), NULL, 0);
+
     baseDir = calculateBaseDir(argv0);
     BAIL_IF_MACRO(baseDir == NULL, NULL, 0);
     ptr = __PHYSFS_platformRealPath(baseDir);
@@ -393,6 +432,7 @@
 } /* PHYSFS_init */
 
 
+/* MAKE SURE you hold stateLock before calling this! */
 static int closeFileHandleList(FileHandleList **list)
 {
     FileHandleList *i;
@@ -417,6 +457,7 @@
 } /* closeFileHandleList */
 
 
+/* MAKE SURE you hold the stateLock before calling this! */
 static void freeSearchPath(void)
 {
     DirInfo *i;
@@ -461,6 +502,11 @@
 
     allowSymLinks = 0;
     initialized = 0;
+
+    __PHYSFS_platformDestroyMutex(errorLock);
+    __PHYSFS_platformDestroyMutex(stateLock);
+
+    errorLock = stateLock = NULL;
     return(1);
 } /* PHYSFS_deinit */
 
@@ -507,49 +553,59 @@
 
 const char *PHYSFS_getWriteDir(void)
 {
-    if (writeDir == NULL)
-        return(NULL);
+    const char *retval = NULL;
 
-    return(writeDir->dirName);
+    __PHYSFS_platformGrabMutex(stateLock);
+    if (writeDir != NULL)
+        retval = writeDir->dirName;
+    __PHYSFS_platformReleaseMutex(stateLock);
+
+    return(retval);
 } /* PHYSFS_getWriteDir */
 
 
 int PHYSFS_setWriteDir(const char *newDir)
 {
+    int retval = 1;
+
+    __PHYSFS_platformGrabMutex(stateLock);
+
     if (writeDir != NULL)
     {
-        BAIL_IF_MACRO(!freeDirInfo(writeDir, openWriteList), NULL, 0);
+        BAIL_IF_MACRO_MUTEX(!freeDirInfo(writeDir, openWriteList), NULL, 
+                            stateLock, 0);
         writeDir = NULL;
     } /* if */
 
     if (newDir != NULL)
     {
         writeDir = buildDirInfo(newDir, 1);
-        return(writeDir != NULL);
+        retval = (writeDir != NULL);
     } /* if */
 
-    return(1);
+    __PHYSFS_platformReleaseMutex(stateLock);
+
+    return(retval);
 } /* PHYSFS_setWriteDir */
 
 
 int PHYSFS_addToSearchPath(const char *newDir, int appendToPath)
 {
     DirInfo *di;
-    DirInfo *i = searchPath;
     DirInfo *prev = NULL;
+    DirInfo *i;
 
-    while (i != NULL)
+    __PHYSFS_platformGrabMutex(stateLock);
+
+    for (i = searchPath; i != NULL; i = i->next)
     {
-        if (strcmp(newDir, i->dirName) == 0)  /* already in search path. */
-            return(1);
-
+        /* already in search path? */
+        BAIL_IF_MACRO_MUTEX(strcmp(newDir, i->dirName)==0, NULL, stateLock, 1);
         prev = i;
-        i = i->next;
-    } /* while */
+    } /* for */
 
     di = buildDirInfo(newDir, 0);
-
-    BAIL_IF_MACRO(di == NULL, NULL, 0);
+    BAIL_IF_MACRO_MUTEX(di == NULL, NULL, stateLock, 0);
 
     if (appendToPath)
     {
@@ -565,6 +621,7 @@
         searchPath = di;
     } /* else */
 
+    __PHYSFS_platformReleaseMutex(stateLock);
     return(1);
 } /* PHYSFS_addToSearchPath */
 
@@ -577,25 +634,26 @@
 
     BAIL_IF_MACRO(oldDir == NULL, ERR_INVALID_ARGUMENT, 0);
 
+    __PHYSFS_platformGrabMutex(stateLock);
     for (i = searchPath; i != NULL; i = i->next)
     {
         if (strcmp(i->dirName, oldDir) == 0)
         {
             next = i->next;
-            BAIL_IF_MACRO(!freeDirInfo(i, openReadList), NULL, 0);
+            BAIL_IF_MACRO_MUTEX(!freeDirInfo(i, openReadList), NULL,
+                                stateLock, 0);
 
             if (prev == NULL)
                 searchPath = next;
             else
                 prev->next = next;
 
-            return(1);
+            BAIL_MACRO_MUTEX(NULL, stateLock, 1);
         } /* if */
         prev = i;
     } /* for */
 
-    __PHYSFS_setError(ERR_NOT_IN_SEARCH_PATH);
-    return(0);
+    BAIL_MACRO_MUTEX(ERR_NOT_IN_SEARCH_PATH, stateLock, 0);
 } /* PHYSFS_removeFromSearchPath */
 
 
@@ -606,11 +664,13 @@
     DirInfo *i;
     char **retval;
 
+    __PHYSFS_platformGrabMutex(stateLock);
+
     for (i = searchPath; i != NULL; i = i->next)
         count++;
 
     retval = (char **) malloc(sizeof (char *) * count);
-    BAIL_IF_MACRO(!retval, ERR_OUT_OF_MEMORY, NULL);
+    BAIL_IF_MACRO_MUTEX(!retval, ERR_OUT_OF_MEMORY, stateLock, NULL);
     count--;
     retval[count] = NULL;
 
@@ -626,13 +686,13 @@
             } /* while */
 
             free(retval);
-            __PHYSFS_setError(ERR_OUT_OF_MEMORY);
-            return(NULL);
+            BAIL_MACRO_MUTEX(ERR_OUT_OF_MEMORY, stateLock, NULL);
         } /* if */
 
         strcpy(retval[x], i->dirName);
     } /* for */
 
+    __PHYSFS_platformReleaseMutex(stateLock);
     return(retval);
 } /* PHYSFS_getSearchPath */
 
@@ -646,6 +706,8 @@
     const char *dirsep = PHYSFS_getDirSeparator();
     char *str;
 
+    BAIL_IF_MACRO(!initialized, ERR_NOT_INITIALIZED, 0);
+
         /* set write dir... */
     str = malloc(strlen(userdir) + (strlen(organization) * 2) +
                  (strlen(appName) * 2) + (strlen(dirsep) * 3) + 2);
@@ -740,7 +802,7 @@
                                               const char *dirName,
                                               const char *append)
 {
-    const char *dirsep = PHYSFS_getDirSeparator();
+    const char *dirsep = __PHYSFS_platformDirSeparator;
     size_t sepsize = strlen(dirsep);
     char *str;
     char *i1;
@@ -852,7 +914,7 @@
 } /* __PHYSFS_verifySecurity */
 
 
-int PHYSFS_mkdir(const char *dirName)
+int PHYSFS_mkdir(const char *dname)
 {
     DirHandle *h;
     char *str;
@@ -860,19 +922,18 @@
     char *end;
     int retval = 0;
 
-    BAIL_IF_MACRO(writeDir == NULL, ERR_NO_WRITE_DIR, 0);
-
-    h = writeDir->dirHandle;
-
-    while (*dirName == '/')
-        dirName++;
+    BAIL_IF_MACRO(dname == NULL, ERR_INVALID_ARGUMENT, 0);
+    while (*dname == '/')
+        dname++;
 
-    BAIL_IF_MACRO(h->funcs->mkdir == NULL, ERR_NOT_SUPPORTED, 0);
-    BAIL_IF_MACRO(!__PHYSFS_verifySecurity(h, dirName), NULL, 0);
-
-    start = str = malloc(strlen(dirName) + 1);
-    BAIL_IF_MACRO(str == NULL, ERR_OUT_OF_MEMORY, 0);
-    strcpy(str, dirName);
+    __PHYSFS_platformGrabMutex(stateLock);
+    BAIL_IF_MACRO_MUTEX(writeDir == NULL, ERR_NO_WRITE_DIR, stateLock, 0);
+    h = writeDir->dirHandle;
+    BAIL_IF_MACRO_MUTEX(!h->funcs->mkdir, ERR_NOT_SUPPORTED, stateLock, 0);
+    BAIL_IF_MACRO_MUTEX(!__PHYSFS_verifySecurity(h, dname), NULL, stateLock, 0);
+    start = str = malloc(strlen(dname) + 1);
+    BAIL_IF_MACRO_MUTEX(str == NULL, ERR_OUT_OF_MEMORY, stateLock, 0);
+    strcpy(str, dname);
 
     while (1)
     {
@@ -891,6 +952,8 @@
         start = end + 1;
     } /* while */
 
+    __PHYSFS_platformReleaseMutex(stateLock);
+
     free(str);
     return(retval);
 } /* PHYSFS_mkdir */
@@ -898,16 +961,23 @@
 
 int PHYSFS_delete(const char *fname)
 {
+    int retval;
     DirHandle *h;
-    BAIL_IF_MACRO(writeDir == NULL, ERR_NO_WRITE_DIR, 0);
-    h = writeDir->dirHandle;
-    BAIL_IF_MACRO(h->funcs->remove == NULL, ERR_NOT_SUPPORTED, 0);
 
+    BAIL_IF_MACRO(fname == NULL, ERR_INVALID_ARGUMENT, 0);
     while (*fname == '/')
         fname++;
 
-    BAIL_IF_MACRO(!__PHYSFS_verifySecurity(h, fname), NULL, 0);
-    return(h->funcs->remove(h, fname));
+    __PHYSFS_platformGrabMutex(stateLock);
+
+    BAIL_IF_MACRO_MUTEX(writeDir == NULL, ERR_NO_WRITE_DIR, stateLock, 0);
+    h = writeDir->dirHandle;
+    BAIL_IF_MACRO_MUTEX(!h->funcs->remove, ERR_NOT_SUPPORTED, stateLock, 0);
+    BAIL_IF_MACRO_MUTEX(!__PHYSFS_verifySecurity(h, fname), NULL, stateLock, 0);
+    retval = h->funcs->remove(h, fname);
+
+    __PHYSFS_platformReleaseMutex(stateLock);
+    return(retval);
 } /* PHYSFS_delete */
 
 
@@ -918,15 +988,20 @@
     while (*filename == '/')
         filename++;
 
+    __PHYSFS_platformGrabMutex(stateLock);
     for (i = searchPath; i != NULL; i = i->next)
     {
         DirHandle *h = i->dirHandle;
         if (__PHYSFS_verifySecurity(h, filename))
         {
             if (h->funcs->exists(h, filename))
+            {
+                __PHYSFS_platformReleaseMutex(stateLock);
                 return(i->dirName);
+            } /* if */
         } /* if */
     } /* for */
+    __PHYSFS_platformReleaseMutex(stateLock);
 
     return(NULL);
 } /* PHYSFS_getRealDir */
@@ -1029,9 +1104,11 @@
     LinkedStringList *finalList = NULL;
     int omitSymLinks = !allowSymLinks;
 
+    BAIL_IF_MACRO(path == NULL, ERR_INVALID_ARGUMENT, NULL);
     while (*path == '/')
         path++;
 
+    __PHYSFS_platformGrabMutex(stateLock);
     for (i = searchPath; i != NULL; i = i->next)
     {
         DirHandle *h = i->dirHandle;
@@ -1041,6 +1118,7 @@
             interpolateStringLists(&finalList, rc);
         } /* if */
     } /* for */
+    __PHYSFS_platformReleaseMutex(stateLock);
 
     retval = convertStringListToPhysFSList(finalList);
     return(retval);
@@ -1049,6 +1127,7 @@
 
 int PHYSFS_exists(const char *fname)
 {
+    BAIL_IF_MACRO(fname == NULL, ERR_INVALID_ARGUMENT, 0);
     while (*fname == '/')
         fname++;
 
@@ -1060,21 +1139,28 @@
 {
     DirInfo *i;
 
+    BAIL_IF_MACRO(fname == NULL, ERR_INVALID_ARGUMENT, 0);
     while (*fname == '/')
         fname++;
 
     if (*fname == '\0')
         return(1);
 
+    __PHYSFS_platformGrabMutex(stateLock);
     for (i = searchPath; i != NULL; i = i->next)
     {
         DirHandle *h = i->dirHandle;
         if (__PHYSFS_verifySecurity(h, fname))
         {
             if (h->funcs->exists(h, fname))
-                return(h->funcs->isDirectory(h, fname));
+            {
+                int retval = h->funcs->isDirectory(h, fname);
+                __PHYSFS_platformReleaseMutex(stateLock);
+                return(retval);
+            } /* if */
         } /* if */
     } /* for */
+    __PHYSFS_platformReleaseMutex(stateLock);
 
     return(0);
 } /* PHYSFS_isDirectory */
@@ -1087,19 +1173,28 @@
     if (!allowSymLinks)
         return(0);
 
+    BAIL_IF_MACRO(fname == NULL, ERR_INVALID_ARGUMENT, 0);
     while (*fname == '/')
         fname++;
 
+    __PHYSFS_platformGrabMutex(stateLock);
     for (i = searchPath; i != NULL; i = i->next)
     {
         DirHandle *h = i->dirHandle;
         if (__PHYSFS_verifySecurity(h, fname))
         {
             if (h->funcs->exists(h, fname))
-                return(h->funcs->isSymLink(h, fname));
+            {
+                int retval = h->funcs->isSymLink(h, fname);
+                __PHYSFS_platformReleaseMutex(stateLock);
+                return(retval);
+            } /* if */
         } /* if */
     } /* for */
 
+/* !!! FIXME: setError ERR_FILE_NOT_FOUND? */
+    __PHYSFS_platformReleaseMutex(stateLock);
+
     return(0);
 } /* PHYSFS_isSymbolicLink */
 
@@ -1108,19 +1203,24 @@
 {
     PHYSFS_file *retval = NULL;
     FileHandle *rc = NULL;
-    DirHandle *h = (writeDir == NULL) ? NULL : writeDir->dirHandle;
-    const DirFunctions *f = (h == NULL) ? NULL : h->funcs;
+    DirHandle *h;
+    const DirFunctions *f;
     FileHandleList *list;
 
+    BAIL_IF_MACRO(fname == NULL, ERR_INVALID_ARGUMENT, NULL);
     while (*fname == '/')
         fname++;
 
-    BAIL_IF_MACRO(!h, ERR_NO_WRITE_DIR, NULL);
-    BAIL_IF_MACRO(!__PHYSFS_verifySecurity(h, fname), NULL, NULL);
+    __PHYSFS_platformGrabMutex(stateLock);
+    h = (writeDir == NULL) ? NULL : writeDir->dirHandle;
+    BAIL_IF_MACRO_MUTEX(!h, ERR_NO_WRITE_DIR, stateLock, NULL);
+    BAIL_IF_MACRO_MUTEX(!__PHYSFS_verifySecurity(h, fname), NULL,
+                        stateLock, NULL);
 
     list = (FileHandleList *) malloc(sizeof (FileHandleList));
-    BAIL_IF_MACRO(!list, ERR_OUT_OF_MEMORY, NULL);
+    BAIL_IF_MACRO_MUTEX(!list, ERR_OUT_OF_MEMORY, stateLock, NULL);
 
+    f = h->funcs;
     rc = (appending) ? f->openAppend(h, fname) : f->openWrite(h, fname);
     if (rc == NULL)
         free(list);
@@ -1132,6 +1232,7 @@
         retval = &(list->handle);
     } /* else */
 
+    __PHYSFS_platformReleaseMutex(stateLock);
     return(retval);
 } /* doOpenWrite */
 
@@ -1150,13 +1251,16 @@
 
 PHYSFS_file *PHYSFS_openRead(const char *fname)
 {
+    PHYSFS_file *retval;
     FileHandle *rc = NULL;
     FileHandleList *list;
     DirInfo *i;
 
+    BAIL_IF_MACRO(fname == NULL, ERR_INVALID_ARGUMENT, NULL);
     while (*fname == '/')
         fname++;
 
+    __PHYSFS_platformGrabMutex(stateLock);
     for (i = searchPath; i != NULL; i = i->next)
     {
         DirHandle *h = i->dirHandle;
@@ -1168,16 +1272,17 @@
         } /* if */
     } /* for */
 
-    if (rc == NULL)
-        return(NULL);
+    BAIL_IF_MACRO_MUTEX(rc == NULL, NULL, stateLock, NULL);
 
     list = (FileHandleList *) malloc(sizeof (FileHandleList));
     BAIL_IF_MACRO(!list, ERR_OUT_OF_MEMORY, NULL);
     list->handle.opaque = (void *) rc;
     list->next = openReadList;
     openReadList = list;
+    retval = &(list->handle);
 
-    return(&(list->handle));
+    __PHYSFS_platformReleaseMutex(stateLock);
+    return(retval);
 } /* PHYSFS_openRead */
 
 
@@ -1215,19 +1320,20 @@
 {
     int rc;
 
+    __PHYSFS_platformGrabMutex(stateLock);
+
     /* -1 == close failure. 0 == not found. 1 == success. */
     rc = closeHandleInOpenList(&openReadList, handle);
-    BAIL_IF_MACRO(rc == -1, NULL, 0);
+    BAIL_IF_MACRO_MUTEX(rc == -1, NULL, stateLock, 0);
     if (!rc)
     {
         rc = closeHandleInOpenList(&openWriteList, handle);
-        BAIL_IF_MACRO(rc == -1, NULL, 0);
+        BAIL_IF_MACRO_MUTEX(rc == -1, NULL, stateLock, 0);
     } /* if */
 
-    if (!rc)
-        __PHYSFS_setError(ERR_NOT_A_HANDLE);
-
-    return(rc);
+    __PHYSFS_platformReleaseMutex(stateLock);
+    BAIL_IF_MACRO(!rc, ERR_NOT_A_HANDLE, 0);
+    return(1);
 } /* PHYSFS_close */
 
 
--- a/physfs_internal.h	Sat Mar 30 01:57:53 2002 +0000
+++ b/physfs_internal.h	Sat Mar 30 16:44:09 2002 +0000
@@ -304,6 +304,8 @@
 /* These get used all over for lessening code clutter. */
 #define BAIL_MACRO(e, r) { __PHYSFS_setError(e); return r; }
 #define BAIL_IF_MACRO(c, e, r) if (c) { __PHYSFS_setError(e); return r; }
+#define BAIL_MACRO_MUTEX(e, m, r) { __PHYSFS_setError(e); __PHYSFS_platformReleaseMutex(m); return r; }
+#define BAIL_IF_MACRO_MUTEX(c, e, m, r) if (c) { __PHYSFS_setError(e); __PHYSFS_platformReleaseMutex(m); return r; }
 
 
 
@@ -633,6 +635,44 @@
 int __PHYSFS_platformDelete(const char *path);
 
 
+/*
+ * Create a platform-specific mutex. This can be whatever datatype your
+ *  platform uses for mutexes, but it is cast to a (void *) for abstractness.
+ *
+ * Return (NULL) if you couldn't create one. Systems without threads can
+ *  return any arbitrary non-NULL value.
+ */
+void *__PHYSFS_platformCreateMutex(void);
+
+/*
+ * Destroy a platform-specific mutex, and clean up any resources associated
+ *  with it. (mutex) is a value previously returned by
+ *  __PHYSFS_platformCreateMutex(). This can be a no-op on single-threaded
+ *  platforms.
+ */
+void __PHYSFS_platformDestroyMutex(void *mutex);
+
+/*
+ * Grab possession of a platform-specific mutex. Mutexes should be recursive;
+ *  that is, the same thread should be able to call this function multiple
+ *  times in a row without causing a deadlock. This function should block 
+ *  until a thread can gain possession of the mutex.
+ *
+ * Return non-zero if the mutex was grabbed, zero if there was an 
+ *  unrecoverable problem grabbing it (this should not be a matter of 
+ *  timing out! We're talking major system errors; block until the mutex 
+ *  is available otherwise.)
+ */
+int __PHYSFS_platformGrabMutex(void *mutex);
+
+/*
+ * Relinquish possession of the mutex when this method has been called 
+ *  once for each time that platformGrabMutex was called. Once possession has
+ *  been released, the next thread in line to grab the mutex (if any) may
+ *  proceed.
+ */
+void __PHYSFS_platformReleaseMutex(void *mutex);
+
 #ifdef __cplusplus
 }
 #endif
--- a/platform/unix.c	Sat Mar 30 01:57:53 2002 +0000
+++ b/platform/unix.c	Sat Mar 30 16:44:09 2002 +0000
@@ -660,5 +660,40 @@
     return(1);
 } /* __PHYSFS_platformDelete */
 
+
+void *__PHYSFS_platformCreateMutex(void)
+{
+    int rc;
+    pthread_mutex_t *m = (pthread_mutex_t *) malloc(sizeof (pthread_mutex_t));
+    BAIL_IF_MACRO(m == NULL, ERR_OUT_OF_MEMORY, NULL);
+    rc = pthread_mutex_init(m, NULL);
+    if (rc != 0)
+    {
+        free(m);
+        BAIL_MACRO(strerror(rc), NULL);
+    } /* if */
+
+    return((void *) m);
+} /* __PHYSFS_platformCreateMutex */
+
+
+void __PHYSFS_platformDestroyMutex(void *mutex)
+{
+    pthread_mutex_destroy((pthread_mutex_t *) mutex);
+    free(mutex);
+} /* __PHYSFS_platformDestroyMutex */
+
+
+int __PHYSFS_platformGrabMutex(void *mutex)
+{
+    return(pthread_mutex_lock((pthread_mutex_t *) mutex) == 0);    
+} /* __PHYSFS_platformGrabMutex */
+
+
+void __PHYSFS_platformReleaseMutex(void *mutex)
+{
+    pthread_mutex_unlock((pthread_mutex_t *) mutex);
+} /* __PHYSFS_platformReleaseMutex */
+
 /* end of unix.c ... */