repl_objset.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  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,
  40. void *iterator);
  41. /*
  42. * Create a new set.
  43. *
  44. * Arguments:
  45. * destructor: a function to be called when an object is to be destroyed
  46. *
  47. * Returns:
  48. * A pointer to the object set, or NULL if an error occured.
  49. */
  50. Repl_Objset *
  51. repl_objset_new(FNFree destructor)
  52. {
  53. Repl_Objset *p;
  54. p = (Repl_Objset *)slapi_ch_malloc(sizeof(Repl_Objset));
  55. p->lock = PR_NewLock();
  56. if (NULL == p->lock) {
  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. now = slapi_current_utc_time();
  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. void *cookie;
  94. PR_Lock((*o)->lock);
  95. if ((co = llistGetFirst((*o)->objects, &cookie)) == NULL) {
  96. really_gone = 1;
  97. PR_Unlock((*o)->lock);
  98. break;
  99. }
  100. while (NULL != co) {
  101. /* Set the deleted flag so the object isn't returned by iterator */
  102. co->flags |= REPL_OBJSET_OBJ_FLAG_DELETED;
  103. if (0 == co->refcnt) {
  104. /* Remove the object */
  105. co = removeCurrentObjectAndGetNextNolock((*o), co, cookie);
  106. } else
  107. co = llistGetNext((*o)->objects, &cookie);
  108. }
  109. PR_Unlock((*o)->lock);
  110. now = slapi_current_utc_time();
  111. if (loopcount > 0) {
  112. DS_Sleep(PR_TicksPerSecond());
  113. }
  114. loopcount++;
  115. }
  116. if (!really_gone) {
  117. if (NULL != panic_fn) {
  118. /*
  119. * Call the "aargh, this thing won't go away" panic
  120. * function for each remaining object.
  121. */
  122. PR_Lock((*o)->lock);
  123. co = llistGetFirst((*o)->objects, &cookie);
  124. while (NULL != co) {
  125. panic_fn(co->data);
  126. co = llistGetNext((*o)->objects, &cookie);
  127. }
  128. PR_Unlock((*o)->lock);
  129. }
  130. } else {
  131. /* Free the linked list */
  132. llistDestroy(&(*o)->objects, (*o)->destructor);
  133. PR_DestroyLock((*o)->lock);
  134. slapi_ch_free((void **)o);
  135. }
  136. }
  137. /*
  138. * Add an object to an object set.
  139. *
  140. * Arguments:
  141. * o: The object set to which the object is to be added.
  142. * name: a null-terminated string that names the object. Must
  143. * be unique.
  144. * obj: pointer to the object to be added.
  145. *
  146. * Return codes:
  147. * REPL_OBJSET_SUCCESS: the item was added to the object set
  148. * REPL_OBJSET_DUPLICATE_KEY: an item with the same key is already
  149. * in the object set.
  150. * REPL_OBJSET_INTERNAL_ERROR: something bad happened.
  151. */
  152. int
  153. repl_objset_add(Repl_Objset *o, const char *name, void *obj)
  154. {
  155. Repl_Objset_object *co = NULL;
  156. Repl_Objset_object *tmp = NULL;
  157. int rc = REPL_OBJSET_SUCCESS;
  158. PR_ASSERT(NULL != o);
  159. PR_ASSERT(NULL != name);
  160. PR_ASSERT(NULL != obj);
  161. PR_Lock(o->lock);
  162. tmp = llistGet(o->objects, name);
  163. if (NULL != tmp) {
  164. rc = REPL_OBJSET_DUPLICATE_KEY;
  165. goto loser;
  166. }
  167. co = (Repl_Objset_object *)slapi_ch_malloc(sizeof(Repl_Objset_object));
  168. co->data = obj;
  169. co->key = slapi_ch_strdup(name);
  170. co->refcnt = 0;
  171. co->flags = 0UL;
  172. if (llistInsertHead(o->objects, name, co) != 0) {
  173. rc = REPL_OBJSET_INTERNAL_ERROR;
  174. goto loser;
  175. }
  176. PR_Unlock(o->lock);
  177. return rc;
  178. loser:
  179. PR_Unlock(o->lock);
  180. if (NULL != co) {
  181. if (NULL != co->key) {
  182. slapi_ch_free((void **)&co->key);
  183. }
  184. slapi_ch_free((void **)&co);
  185. }
  186. return rc;
  187. }
  188. /* Must be called with the repl_objset locked */
  189. static void
  190. removeObjectNolock(Repl_Objset *o, Repl_Objset_object *co)
  191. {
  192. /* Remove from list */
  193. llistRemove(o->objects, co->key);
  194. /* Destroy the object */
  195. o->destructor(&(co->data));
  196. slapi_ch_free((void **)&(co->key));
  197. /* Deallocate the container */
  198. slapi_ch_free((void **)&co);
  199. }
  200. static Repl_Objset_object *
  201. removeCurrentObjectAndGetNextNolock(Repl_Objset *o, Repl_Objset_object *co, void *iterator)
  202. {
  203. Repl_Objset_object *ro;
  204. PR_ASSERT(o);
  205. PR_ASSERT(co);
  206. PR_ASSERT(iterator);
  207. ro = llistRemoveCurrentAndGetNext(o->objects, &iterator);
  208. o->destructor(&(co->data));
  209. slapi_ch_free((void **)&(co->key));
  210. /* Deallocate the container */
  211. slapi_ch_free((void **)&co);
  212. return ro;
  213. }
  214. /* Must be called with the repl_objset locked */
  215. static void
  216. acquireNoLock(Repl_Objset_object *co)
  217. {
  218. co->refcnt++;
  219. }
  220. /* Must be called with the repl_objset locked */
  221. static void
  222. releaseNoLock(Repl_Objset *o, Repl_Objset_object *co)
  223. {
  224. PR_ASSERT(co->refcnt >= 1);
  225. if (--co->refcnt == 0) {
  226. if (co->flags & REPL_OBJSET_OBJ_FLAG_DELETED) {
  227. /* Remove the object */
  228. removeObjectNolock(o, co);
  229. }
  230. }
  231. }
  232. /*
  233. * Retrieve an object from the object set. If an object with
  234. * the given key is found, its reference count is incremented,
  235. * a pointer to the object is returned, and a handle to use
  236. * to refer to the object is returned.
  237. *
  238. * Arguments:
  239. * o: The object set to be searched.
  240. * key: key of the object to be retrieved
  241. * obj: pointer to void * that will be set to point to the
  242. * object, if found.
  243. * handle: pointer to void * that will be set to point to a
  244. * handle, used to refer to the object, if found.
  245. *
  246. * Returns:
  247. * REPL_OBJSET_SUCCESS: an item was found.
  248. * REPL_OBJSET_KEY_NOT_FOUND: no item with the given key was found.
  249. */
  250. int
  251. repl_objset_acquire(Repl_Objset *o, const char *key, void **obj, void **handle)
  252. {
  253. Repl_Objset_object *co = NULL;
  254. int rc = REPL_OBJSET_KEY_NOT_FOUND;
  255. PR_ASSERT(NULL != o);
  256. PR_ASSERT(NULL != key);
  257. PR_ASSERT(NULL != obj);
  258. PR_ASSERT(NULL != handle);
  259. PR_Lock(o->lock);
  260. co = llistGet(o->objects, key);
  261. if (NULL != co && !(co->flags & REPL_OBJSET_OBJ_FLAG_DELETED)) {
  262. acquireNoLock(co);
  263. *obj = (void *)co->data;
  264. *handle = (void *)co;
  265. rc = REPL_OBJSET_SUCCESS;
  266. }
  267. PR_Unlock(o->lock);
  268. return rc;
  269. }
  270. /*
  271. * Return an object to the object set.
  272. *
  273. * Arguments:
  274. * o: The object set containing the objct
  275. * handle: reference to the object.
  276. *
  277. */
  278. void
  279. repl_objset_release(Repl_Objset *o, void *handle)
  280. {
  281. Repl_Objset_object *co;
  282. PR_ASSERT(NULL != o);
  283. PR_ASSERT(NULL != handle);
  284. co = (Repl_Objset_object *)handle;
  285. PR_Lock(o->lock);
  286. releaseNoLock(o, co);
  287. PR_Unlock(o->lock);
  288. }
  289. /*
  290. * Delete an object from the object set
  291. *
  292. * Arguments:
  293. * o: The object set containing the object.
  294. * handle: reference to the object.
  295. */
  296. void
  297. repl_objset_delete(Repl_Objset *o, void *handle)
  298. {
  299. Repl_Objset_object *co = (Repl_Objset_object *)handle;
  300. PR_ASSERT(NULL != o);
  301. PR_ASSERT(NULL != co);
  302. PR_Lock(o->lock);
  303. if (co->refcnt == 0) {
  304. removeObjectNolock(o, co);
  305. } else {
  306. /* Set deleted flag, clean up later */
  307. co->flags |= REPL_OBJSET_OBJ_FLAG_DELETED;
  308. }
  309. PR_Unlock(o->lock);
  310. }
  311. typedef struct _iterator
  312. {
  313. Repl_Objset *o; /* set for which iterator was created */
  314. void *cookie; /* for linked list */
  315. Repl_Objset_object *co; /* our wrapper */
  316. } iterator;
  317. /*
  318. * Get the first object in an object set.
  319. * Used when enumerating all of the objects in a set.
  320. * Arguments:
  321. * o: The object set being enumerated
  322. * itcontext: an iteration context, to be passed back to subsequent calls
  323. * to repl_objset_next_object.
  324. * handle: a pointer to pointer to void. This will be filled in with
  325. * a reference to the object's enclosing object.
  326. * Returns:
  327. * A pointer to the next object in the set, or NULL if there are no
  328. * objects in the set.
  329. *
  330. */
  331. void *
  332. repl_objset_first_object(Repl_Objset *o, void **itcontext, void **handle)
  333. {
  334. Repl_Objset_object *co = NULL;
  335. void *cookie;
  336. void *retptr = NULL;
  337. iterator *it;
  338. PR_ASSERT(NULL != o);
  339. PR_ASSERT(NULL != itcontext);
  340. *itcontext = NULL;
  341. if (NULL == o->objects) {
  342. return (NULL);
  343. }
  344. /* Find the first non-deleted object */
  345. PR_Lock(o->lock);
  346. co = llistGetFirst(o->objects, &cookie);
  347. while (NULL != co && (co->flags & REPL_OBJSET_OBJ_FLAG_DELETED)) {
  348. co = llistGetNext(o->objects, &cookie);
  349. }
  350. if (NULL != co) {
  351. /* Increment refcnt until item given back to us */
  352. acquireNoLock(co);
  353. /* Save away context */
  354. it = (iterator *)slapi_ch_malloc(sizeof(iterator));
  355. *itcontext = it;
  356. it->o = o;
  357. it->cookie = cookie;
  358. it->co = co;
  359. retptr = co->data;
  360. }
  361. PR_Unlock(o->lock);
  362. if (NULL != handle) {
  363. *handle = co;
  364. }
  365. return retptr;
  366. }
  367. /*
  368. * Get the next object in the set.
  369. * Arguments:
  370. * o: The object set being enumerated
  371. * itcontext: an iteration context, to be passed back to subsequent calls
  372. * to repl_objset_next_object.
  373. * handle: a pointer to pointer to void. This will be filled in with
  374. * a reference to the object's enclosing object.
  375. *
  376. * Returns:
  377. * A pointer to the next object in the set, or NULL if there are no more
  378. * objects.
  379. */
  380. void *
  381. repl_objset_next_object(Repl_Objset *o, void *itcontext, void **handle)
  382. {
  383. Repl_Objset_object *co = NULL;
  384. Repl_Objset_object *tmp_co;
  385. void *retptr = NULL;
  386. iterator *it = (iterator *)itcontext;
  387. PR_ASSERT(NULL != o);
  388. PR_ASSERT(NULL != it);
  389. PR_ASSERT(NULL != it->co);
  390. PR_Lock(o->lock);
  391. tmp_co = it->co;
  392. /* Find the next non-deleted object */
  393. while ((co = llistGetNext(o->objects, &it->cookie)) != NULL &&
  394. !(co->flags & REPL_OBJSET_OBJ_FLAG_DELETED))
  395. ;
  396. if (NULL != co) {
  397. acquireNoLock(co);
  398. it->co = co;
  399. retptr = co->data;
  400. } else {
  401. /*
  402. * No more non-deleted objects - erase context (freeing
  403. * it is responsibility of caller.
  404. */
  405. it->cookie = NULL;
  406. it->co = NULL;
  407. }
  408. releaseNoLock(o, tmp_co);
  409. PR_Unlock(o->lock);
  410. if (NULL != handle) {
  411. *handle = co;
  412. }
  413. return retptr;
  414. }
  415. /*
  416. * Destroy an itcontext iterator
  417. */
  418. void
  419. repl_objset_iterator_destroy(void **itcontext)
  420. {
  421. if (NULL != itcontext && NULL != *itcontext) {
  422. /* check if we did not iterate through the entire list
  423. and need to release last accessed element */
  424. iterator *it = *(iterator **)itcontext;
  425. if (it->co)
  426. repl_objset_release(it->o, it->co);
  427. slapi_ch_free((void **)itcontext);
  428. }
  429. }