repl_objset.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. /** BEGIN COPYRIGHT BLOCK
  2. * Copyright 2001 Sun Microsystems, Inc.
  3. * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
  4. * All rights reserved.
  5. * END COPYRIGHT BLOCK **/
  6. /* repl_objset.c */
  7. /*
  8. * Support for lifetime management of sets of objects.
  9. * Objects are refcounted. NOTE: this API is deprecated.
  10. * Use the object/objset API provided by libslapd.
  11. */
  12. #include "slapi-plugin.h"
  13. #include "slapi-private.h"
  14. #include "repl_objset.h"
  15. #include <prlock.h>
  16. #define REPL_OBJSET_OBJ_FLAG_DELETED 0x1
  17. typedef struct repl_objset_object
  18. {
  19. void *data; /* pointer to actual node data */
  20. char *key; /* key for this object. null-terminated string */
  21. int refcnt; /* reference count for this object */
  22. unsigned long flags; /* state of this object */
  23. } Repl_Objset_object;
  24. typedef struct repl_objset
  25. {
  26. LList *objects;
  27. FNFree destructor; /* destructor for objects - provided by caller */
  28. PRLock *lock;
  29. } repl_objset;
  30. /* Forward declarations */
  31. static void removeObjectNolock(Repl_Objset *o, Repl_Objset_object *co);
  32. static Repl_Objset_object *removeCurrentObjectAndGetNextNolock (Repl_Objset *o,
  33. Repl_Objset_object *co, void *iterator);
  34. /*
  35. * Create a new set.
  36. *
  37. * Arguments:
  38. * destructor: a function to be called when an object is to be destroyed
  39. *
  40. * Returns:
  41. * A pointer to the object set, or NULL if an error occured.
  42. */
  43. Repl_Objset *
  44. repl_objset_new(FNFree destructor)
  45. {
  46. Repl_Objset *p;
  47. p = (Repl_Objset *)slapi_ch_malloc(sizeof(Repl_Objset));
  48. p->lock = PR_NewLock();
  49. if (NULL == p->lock)
  50. {
  51. free(p); p = NULL;
  52. }
  53. p->objects = llistNew();
  54. p->destructor = destructor;
  55. return p;
  56. }
  57. /*
  58. * Destroy a Repl_Objset.
  59. * Arguments:
  60. * o: the object set to be destroyed
  61. * maxwait: the maximum time to wait for all object refcnts to
  62. * go to zero.
  63. * panic_fn: a function to be called if, after waiting "maxwait"
  64. * seconds, not all object refcnts are zero.
  65. * The caller must ensure that no one else holds references to the
  66. * set or any objects it contains.
  67. */
  68. void
  69. repl_objset_destroy(Repl_Objset **o, time_t maxwait, FNFree panic_fn)
  70. {
  71. Repl_Objset_object *co = NULL;
  72. time_t now, stop_time;
  73. int really_gone;
  74. int loopcount;
  75. void *cookie;
  76. PR_ASSERT(NULL != o);
  77. PR_ASSERT(NULL != *o);
  78. time(&now);
  79. stop_time = now + maxwait;
  80. /*
  81. * Loop over the objects until they all are actually gone,
  82. * or until maxwait seconds have passed.
  83. */
  84. really_gone = 0;
  85. loopcount = 0;
  86. while (now < stop_time)
  87. {
  88. void *cookie;
  89. PR_Lock((*o)->lock);
  90. if ((co = llistGetFirst((*o)->objects, &cookie)) == NULL)
  91. {
  92. really_gone = 1;
  93. PR_Unlock((*o)->lock);
  94. break;
  95. }
  96. while (NULL != co)
  97. {
  98. /* Set the deleted flag so the object isn't returned by iterator */
  99. co->flags |= REPL_OBJSET_OBJ_FLAG_DELETED;
  100. if (0 == co->refcnt)
  101. {
  102. /* Remove the object */
  103. co = removeCurrentObjectAndGetNextNolock ((*o), co, cookie);
  104. }
  105. else
  106. co = llistGetNext((*o)->objects, &cookie);
  107. }
  108. PR_Unlock((*o)->lock);
  109. time(&now);
  110. if (loopcount > 0)
  111. {
  112. DS_Sleep(PR_TicksPerSecond());
  113. }
  114. loopcount++;
  115. }
  116. if (!really_gone)
  117. {
  118. if (NULL != panic_fn)
  119. {
  120. /*
  121. * Call the "aargh, this thing won't go away" panic
  122. * function for each remaining object.
  123. */
  124. PR_Lock((*o)->lock);
  125. if ((co = llistGetFirst((*o)->objects, &cookie)) == NULL)
  126. {
  127. panic_fn(co->data);
  128. while (NULL != co)
  129. {
  130. panic_fn(co->data);
  131. co = llistGetNext((*o)->objects, &cookie);
  132. }
  133. }
  134. PR_Unlock((*o)->lock);
  135. }
  136. }
  137. else
  138. {
  139. /* Free the linked list */
  140. llistDestroy(&(*o)->objects, (*o)->destructor);
  141. PR_DestroyLock((*o)->lock);
  142. free(*o); *o = NULL;
  143. }
  144. }
  145. /*
  146. * Add an object to an object set.
  147. *
  148. * Arguments:
  149. * o: The object set to which the object is to be added.
  150. * name: a null-terminated string that names the object. Must
  151. * be unique.
  152. * obj: pointer to the object to be added.
  153. *
  154. * Return codes:
  155. * REPL_OBJSET_SUCCESS: the item was added to the object set
  156. * REPL_OBJSET_DUPLICATE_KEY: an item with the same key is already
  157. * in the object set.
  158. * REPL_OBJSET_INTERNAL_ERROR: something bad happened.
  159. */
  160. int
  161. repl_objset_add(Repl_Objset *o, const char *name, void *obj)
  162. {
  163. Repl_Objset_object *co = NULL;
  164. Repl_Objset_object *tmp = NULL;
  165. int rc = REPL_OBJSET_SUCCESS;
  166. PR_ASSERT(NULL != o);
  167. PR_ASSERT(NULL != name);
  168. PR_ASSERT(NULL != obj);
  169. PR_Lock(o->lock);
  170. tmp = llistGet(o->objects, name);
  171. if (NULL != tmp)
  172. {
  173. rc = REPL_OBJSET_DUPLICATE_KEY;
  174. goto loser;
  175. }
  176. co = (Repl_Objset_object *)slapi_ch_malloc(sizeof(Repl_Objset_object));
  177. co->data = obj;
  178. co->key = slapi_ch_strdup(name);
  179. co->refcnt = 0;
  180. co->flags = 0UL;
  181. if (llistInsertHead(o->objects, name, co) != 0)
  182. {
  183. rc = REPL_OBJSET_INTERNAL_ERROR;
  184. goto loser;
  185. }
  186. PR_Unlock(o->lock);
  187. return rc;
  188. loser:
  189. PR_Unlock(o->lock);
  190. if (NULL != co)
  191. {
  192. if (NULL != co->key)
  193. {
  194. slapi_ch_free((void **)&co->key);
  195. }
  196. slapi_ch_free((void **)&co);
  197. }
  198. return rc;
  199. }
  200. /* Must be called with the repl_objset locked */
  201. static void
  202. removeObjectNolock(Repl_Objset *o, Repl_Objset_object *co)
  203. {
  204. /* Remove from list */
  205. llistRemove(o->objects, co->key);
  206. /* Destroy the object */
  207. o->destructor(&(co->data));
  208. free(co->key);
  209. /* Deallocate the container */
  210. free(co);
  211. }
  212. static Repl_Objset_object *
  213. removeCurrentObjectAndGetNextNolock (Repl_Objset *o, Repl_Objset_object *co, void *iterator)
  214. {
  215. Repl_Objset_object *ro;
  216. PR_ASSERT (o);
  217. PR_ASSERT (co);
  218. PR_ASSERT (iterator);
  219. ro = llistRemoveCurrentAndGetNext (o->objects, &iterator);
  220. o->destructor(&(co->data));
  221. free(co->key);
  222. /* Deallocate the container */
  223. free(co);
  224. return ro;
  225. }
  226. /* Must be called with the repl_objset locked */
  227. static void
  228. acquireNoLock(Repl_Objset_object *co)
  229. {
  230. co->refcnt++;
  231. }
  232. /* Must be called with the repl_objset locked */
  233. static void
  234. releaseNoLock(Repl_Objset *o, Repl_Objset_object *co)
  235. {
  236. PR_ASSERT(co->refcnt >= 1);
  237. if (--co->refcnt == 0)
  238. {
  239. if (co->flags & REPL_OBJSET_OBJ_FLAG_DELETED)
  240. {
  241. /* Remove the object */
  242. removeObjectNolock(o, co);
  243. }
  244. }
  245. }
  246. /*
  247. * Retrieve an object from the object set. If an object with
  248. * the given key is found, its reference count is incremented,
  249. * a pointer to the object is returned, and a handle to use
  250. * to refer to the object is returned.
  251. *
  252. * Arguments:
  253. * o: The object set to be searched.
  254. * key: key of the object to be retrieved
  255. * obj: pointer to void * that will be set to point to the
  256. * object, if found.
  257. * handle: pointer to void * that will be set to point to a
  258. * handle, used to refer to the object, if found.
  259. *
  260. * Returns:
  261. * REPL_OBJSET_SUCCESS: an item was found.
  262. * REPL_OBJSET_KEY_NOT_FOUND: no item with the given key was found.
  263. */
  264. int
  265. repl_objset_acquire(Repl_Objset *o, const char *key, void **obj, void **handle)
  266. {
  267. Repl_Objset_object *co = NULL;
  268. int rc = REPL_OBJSET_KEY_NOT_FOUND;
  269. PR_ASSERT(NULL != o);
  270. PR_ASSERT(NULL != key);
  271. PR_ASSERT(NULL != obj);
  272. PR_ASSERT(NULL != handle);
  273. PR_Lock(o->lock);
  274. co = llistGet(o->objects, key);
  275. if (NULL != co && !(co->flags & REPL_OBJSET_OBJ_FLAG_DELETED))
  276. {
  277. acquireNoLock(co);
  278. *obj = (void *)co->data;
  279. *handle = (void *)co;
  280. rc = REPL_OBJSET_SUCCESS;
  281. }
  282. PR_Unlock(o->lock);
  283. return rc;
  284. }
  285. /*
  286. * Return an object to the object set.
  287. *
  288. * Arguments:
  289. * o: The object set containing the objct
  290. * handle: reference to the object.
  291. *
  292. */
  293. void
  294. repl_objset_release(Repl_Objset *o, void *handle)
  295. {
  296. Repl_Objset_object *co;
  297. PR_ASSERT(NULL != o);
  298. PR_ASSERT(NULL != handle);
  299. co = (Repl_Objset_object *)handle;
  300. PR_Lock(o->lock);
  301. releaseNoLock(o, co);
  302. PR_Unlock(o->lock);
  303. }
  304. /*
  305. * Delete an object from the object set
  306. *
  307. * Arguments:
  308. * o: The object set containing the object.
  309. * handle: reference to the object.
  310. */
  311. void
  312. repl_objset_delete(Repl_Objset *o, void *handle)
  313. {
  314. Repl_Objset_object *co = (Repl_Objset_object *)handle;
  315. PR_ASSERT(NULL != o);
  316. PR_ASSERT(NULL != co);
  317. PR_Lock(o->lock);
  318. if (co->refcnt == 0)
  319. {
  320. removeObjectNolock(o, co);
  321. }
  322. else
  323. {
  324. /* Set deleted flag, clean up later */
  325. co->flags |= REPL_OBJSET_OBJ_FLAG_DELETED;
  326. }
  327. PR_Unlock(o->lock);
  328. }
  329. typedef struct _iterator
  330. {
  331. Repl_Objset *o; /* set for which iterator was created */
  332. void *cookie; /* for linked list */
  333. Repl_Objset_object *co; /* our wrapper */
  334. } iterator;
  335. /*
  336. * Get the first object in an object set.
  337. * Used when enumerating all of the objects in a set.
  338. * Arguments:
  339. * o: The object set being enumerated
  340. * itcontext: an iteration context, to be passed back to subsequent calls
  341. * to repl_objset_next_object.
  342. * handle: a pointer to pointer to void. This will be filled in with
  343. * a reference to the object's enclosing object.
  344. * Returns:
  345. * A pointer to the next object in the set, or NULL if there are no
  346. * objects in the set.
  347. *
  348. */
  349. void *
  350. repl_objset_first_object(Repl_Objset *o, void **itcontext, void **handle)
  351. {
  352. Repl_Objset_object *co = NULL;
  353. void *cookie;
  354. void *retptr = NULL;
  355. iterator *it;
  356. PR_ASSERT(NULL != o);
  357. PR_ASSERT(NULL != itcontext);
  358. *itcontext = NULL;
  359. if (NULL == o->objects) {
  360. return(NULL);
  361. }
  362. /* Find the first non-deleted object */
  363. PR_Lock(o->lock);
  364. co = llistGetFirst(o->objects, &cookie);
  365. while (NULL != co && (co->flags & REPL_OBJSET_OBJ_FLAG_DELETED))
  366. {
  367. co = llistGetNext(o->objects, &cookie);
  368. }
  369. if (NULL != co)
  370. {
  371. /* Increment refcnt until item given back to us */
  372. acquireNoLock(co);
  373. /* Save away context */
  374. it = (iterator *)slapi_ch_malloc(sizeof(iterator));
  375. *itcontext = it;
  376. it->o = o;
  377. it->cookie = cookie;
  378. it->co = co;
  379. retptr = co->data;
  380. }
  381. PR_Unlock(o->lock);
  382. if (NULL != handle)
  383. {
  384. *handle = co;
  385. }
  386. return retptr;
  387. }
  388. /*
  389. * Get the next object in the set.
  390. * Arguments:
  391. * o: The object set being enumerated
  392. * itcontext: an iteration context, to be passed back to subsequent calls
  393. * to repl_objset_next_object.
  394. * handle: a pointer to pointer to void. This will be filled in with
  395. * a reference to the object's enclosing object.
  396. *
  397. * Returns:
  398. * A pointer to the next object in the set, or NULL if there are no more
  399. * objects.
  400. */
  401. void *
  402. repl_objset_next_object(Repl_Objset *o, void *itcontext, void **handle)
  403. {
  404. Repl_Objset_object *co = NULL;
  405. Repl_Objset_object *tmp_co;
  406. void *retptr = NULL;
  407. iterator *it = (iterator *)itcontext;
  408. PR_ASSERT(NULL != o);
  409. PR_ASSERT(NULL != it);
  410. PR_ASSERT(NULL != it->co);
  411. PR_Lock(o->lock);
  412. tmp_co = it->co;
  413. /* Find the next non-deleted object */
  414. while ((co = llistGetNext(o->objects, &it->cookie)) != NULL &&
  415. !(co->flags & REPL_OBJSET_OBJ_FLAG_DELETED));
  416. if (NULL != co)
  417. {
  418. acquireNoLock(co);
  419. it->co = co;
  420. retptr = co->data;
  421. }
  422. else
  423. {
  424. /*
  425. * No more non-deleted objects - erase context (freeing
  426. * it is responsibility of caller.
  427. */
  428. it->cookie = NULL;
  429. it->co = NULL;
  430. }
  431. releaseNoLock(o, tmp_co);
  432. PR_Unlock(o->lock);
  433. if (NULL != handle)
  434. {
  435. *handle = co;
  436. }
  437. return retptr;
  438. }
  439. /*
  440. * Destroy an itcontext iterator
  441. */
  442. void
  443. repl_objset_iterator_destroy(void **itcontext)
  444. {
  445. if (NULL != itcontext && NULL != *itcontext)
  446. {
  447. /* check if we did not iterate through the entire list
  448. and need to release last accessed element */
  449. iterator *it = *(iterator**)itcontext;
  450. if (it->co)
  451. repl_objset_release (it->o, it->co);
  452. slapi_ch_free((void **)itcontext);
  453. }
  454. }