Back to index

lightning-sunbird  0.9+nobinonly
ipcLockModule.cpp
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla IPC.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 2002
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *   Darin Fisher <darin@netscape.com>
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 #include <stdlib.h>
00039 #include <stdio.h>
00040 #include "ipcModuleUtil.h"
00041 #include "ipcLockProtocol.h"
00042 #include "plhash.h"
00043 #include "plstr.h"
00044 
00045 #ifdef DEBUG
00046 #define LOG(args) printf args
00047 #else
00048 #define LOG(args)
00049 #endif
00050 
00051 static const nsID kLockTargetID = IPC_LOCK_TARGETID;
00052 
00053 static void
00054 ipcLockModule_Send(PRUint32 cid, const char *key, PRUint8 opcode)
00055 {
00056     ipcLockMsg msg = { opcode, 0, key };
00057     PRUint32 bufLen;
00058     PRUint8 *buf = IPC_FlattenLockMsg(&msg, &bufLen);
00059     if (!buf)
00060         return;
00061     IPC_SendMsg(cid, kLockTargetID, buf, bufLen);
00062     free(buf);
00063 }
00064 
00065 //-----------------------------------------------------------------------------
00066 
00067 //
00068 // gLockTable stores mapping from lock name to ipcLockContext
00069 //
00070 static PLHashTable *gLockTable = NULL;
00071 
00072 //-----------------------------------------------------------------------------
00073 
00074 struct ipcLockContext
00075 {
00076     PRUint32               mOwnerID;     // client ID of this lock's owner
00077     struct ipcLockContext *mNextPending; // pointer to client next in line to
00078                                          // acquire this lock.
00079 
00080     ipcLockContext(PRUint32 ownerID)
00081         : mOwnerID(ownerID)
00082         , mNextPending(NULL) {}
00083 };
00084 
00085 //-----------------------------------------------------------------------------
00086 
00087 PR_STATIC_CALLBACK(void *)
00088 ipcLockModule_AllocTable(void *pool, PRSize size)
00089 {
00090     return malloc(size);
00091 }
00092 
00093 PR_STATIC_CALLBACK(void)
00094 ipcLockModule_FreeTable(void *pool, void *item)
00095 {
00096     free(item);
00097 }
00098 
00099 PR_STATIC_CALLBACK(PLHashEntry *)
00100 ipcLockModule_AllocEntry(void *pool, const void *key)
00101 {
00102     return (PLHashEntry *) malloc(sizeof(PLHashEntry));
00103 }
00104 
00105 PR_STATIC_CALLBACK(void)
00106 ipcLockModule_FreeEntry(void *pool, PLHashEntry *he, PRUintn flag)
00107 {
00108     PL_strfree((char *) he->key);
00109     free(he);
00110 }
00111 
00112 static const PLHashAllocOps ipcLockModule_AllocOps = {
00113     ipcLockModule_AllocTable,
00114     ipcLockModule_FreeTable,
00115     ipcLockModule_AllocEntry,
00116     ipcLockModule_FreeEntry
00117 };
00118 
00119 //-----------------------------------------------------------------------------
00120 
00121 static void
00122 ipcLockModule_AcquireLock(PRUint32 cid, PRUint8 flags, const char *key)
00123 {
00124     LOG(("$$$ acquiring lock [key=%s]\n", key));
00125 
00126     if (!gLockTable)
00127         return;
00128 
00129     ipcLockContext *ctx;
00130     
00131     ctx = (ipcLockContext *) PL_HashTableLookup(gLockTable, key);
00132     if (ctx) {
00133         //
00134         // lock is already acquired, add this client to the queue.  make
00135         // sure this client doesn't already own the lock or live on the queue.
00136         //
00137         while (ctx->mOwnerID != cid && ctx->mNextPending)
00138             ctx = ctx->mNextPending;
00139         if (ctx->mOwnerID != cid) {
00140             //
00141             // if nonblocking, then send busy status message.  otherwise,
00142             // proceed to add this client to the pending queue.
00143             //
00144             if (flags & IPC_LOCK_FL_NONBLOCKING)
00145                 ipcLockModule_Send(cid, key, IPC_LOCK_OP_STATUS_BUSY);
00146             else
00147                 ctx->mNextPending = new ipcLockContext(cid);
00148         }
00149     }
00150     else {
00151         //
00152         // ok, add this lock to the table, and notify client that it now owns
00153         // the lock!
00154         //
00155         ctx = new ipcLockContext(cid);
00156         if (!ctx)
00157             return;
00158 
00159         PL_HashTableAdd(gLockTable, PL_strdup(key), ctx);
00160 
00161         ipcLockModule_Send(cid, key, IPC_LOCK_OP_STATUS_ACQUIRED);
00162     }
00163 }
00164 
00165 static PRBool
00166 ipcLockModule_ReleaseLockHelper(PRUint32 cid, const char *key, ipcLockContext *ctx)
00167 {
00168     LOG(("$$$ releasing lock [key=%s]\n", key));
00169 
00170     PRBool removeEntry = PR_FALSE;
00171 
00172     //
00173     // lock is already acquired _or_ maybe client is on the pending list.
00174     //
00175     if (ctx->mOwnerID == cid) {
00176         if (ctx->mNextPending) {
00177             //
00178             // remove this element from the list.  since this is the
00179             // first element in the list, instead of removing it we
00180             // shift the data from the next context into this one and
00181             // delete the next context.
00182             //
00183             ipcLockContext *next = ctx->mNextPending;
00184             ctx->mOwnerID = next->mOwnerID;
00185             ctx->mNextPending = next->mNextPending;
00186             delete next;
00187             //
00188             // notify client that it now owns the lock
00189             //
00190             ipcLockModule_Send(ctx->mOwnerID, key, IPC_LOCK_OP_STATUS_ACQUIRED);
00191         }
00192         else {
00193             delete ctx;
00194             removeEntry = PR_TRUE;
00195         }
00196     }
00197     else {
00198         ipcLockContext *prev;
00199         for (;;) {
00200             prev = ctx;
00201             ctx = ctx->mNextPending;
00202             if (!ctx)
00203                 break;
00204             if (ctx->mOwnerID == cid) {
00205                 // remove ctx from list
00206                 prev->mNextPending = ctx->mNextPending;
00207                 delete ctx;
00208                 break;
00209             }
00210         }
00211     }
00212 
00213     return removeEntry;
00214 }
00215 
00216 static void
00217 ipcLockModule_ReleaseLock(PRUint32 cid, const char *key)
00218 {
00219     if (!gLockTable)
00220         return;
00221 
00222     ipcLockContext *ctx;
00223 
00224     ctx = (ipcLockContext *) PL_HashTableLookup(gLockTable, key);
00225     if (ctx && ipcLockModule_ReleaseLockHelper(cid, key, ctx))
00226         PL_HashTableRemove(gLockTable, key);
00227 }
00228 
00229 PR_STATIC_CALLBACK(PRIntn)
00230 ipcLockModule_ReleaseByCID(PLHashEntry *he, PRIntn i, void *arg)
00231 {
00232     PRUint32 cid = *(PRUint32 *) arg;
00233 
00234     ipcLockContext *ctx = (ipcLockContext *) he->value;
00235     if (ctx->mOwnerID != cid)
00236         return HT_ENUMERATE_NEXT;
00237 
00238     LOG(("$$$ ipcLockModule_ReleaseByCID [cid=%u key=%s he=%p]\n",
00239         cid, (char*)he->key, (void*)he));
00240 
00241     if (ipcLockModule_ReleaseLockHelper(cid, (const char *) he->key, ctx))
00242         return HT_ENUMERATE_REMOVE;
00243 
00244     return HT_ENUMERATE_NEXT;
00245 }
00246 
00247 //-----------------------------------------------------------------------------
00248 
00249 static void
00250 ipcLockModule_Init()
00251 {
00252     LOG(("$$$ ipcLockModule_Init\n"));
00253 
00254     gLockTable = PL_NewHashTable(32,
00255                                  PL_HashString,
00256                                  PL_CompareStrings,
00257                                  PL_CompareValues,
00258                                  &ipcLockModule_AllocOps,
00259                                  NULL);
00260 }
00261 
00262 static void
00263 ipcLockModule_Shutdown()
00264 {
00265     LOG(("$$$ ipcLockModule_Shutdown\n"));
00266     
00267     if (gLockTable) {
00268         // XXX walk table destroying all ipcLockContext objects
00269  
00270         PL_HashTableDestroy(gLockTable);
00271         gLockTable = NULL;
00272     }
00273 }
00274 
00275 static void
00276 ipcLockModule_HandleMsg(ipcClientHandle client,
00277                         const nsID     &target,
00278                         const void     *data,
00279                         PRUint32        dataLen)
00280 {
00281     PRUint32 cid = IPC_GetClientID(client);
00282 
00283     LOG(("$$$ ipcLockModule_HandleMsg [cid=%u]\n", cid));
00284 
00285     ipcLockMsg msg;
00286     IPC_UnflattenLockMsg((const PRUint8 *) data, dataLen, &msg);
00287 
00288     switch (msg.opcode) {
00289     case IPC_LOCK_OP_ACQUIRE:
00290         ipcLockModule_AcquireLock(cid, msg.flags, msg.key);
00291         break;
00292     case IPC_LOCK_OP_RELEASE:
00293         ipcLockModule_ReleaseLock(cid, msg.key);
00294         break;
00295     default:
00296         PR_NOT_REACHED("invalid opcode");
00297     }
00298 }
00299 
00300 static void
00301 ipcLockModule_ClientUp(ipcClientHandle client)
00302 {
00303     LOG(("$$$ ipcLockModule_ClientUp [%u]\n", IPC_GetClientID(client)));
00304 }
00305 
00306 static void
00307 ipcLockModule_ClientDown(ipcClientHandle client)
00308 {
00309     PRUint32 cid = IPC_GetClientID(client);
00310 
00311     LOG(("$$$ ipcLockModule_ClientDown [%u]\n", cid));
00312 
00313     //
00314     // enumerate lock table, release any locks held by this client.
00315     //
00316 
00317     PL_HashTableEnumerateEntries(gLockTable, ipcLockModule_ReleaseByCID, &cid);
00318 }
00319 
00320 //-----------------------------------------------------------------------------
00321 
00322 static ipcModuleMethods gLockMethods =
00323 {
00324     IPC_MODULE_METHODS_VERSION,
00325     ipcLockModule_Init,
00326     ipcLockModule_Shutdown,
00327     ipcLockModule_HandleMsg,
00328     ipcLockModule_ClientUp,
00329     ipcLockModule_ClientDown
00330 };
00331 
00332 static ipcModuleEntry gLockModuleEntry[] =
00333 {
00334     { IPC_LOCK_TARGETID, &gLockMethods }
00335 };
00336 
00337 IPC_IMPL_GETMODULES(ipcLockModule, gLockModuleEntry)