repl_objset.c 11 KB

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