1
0

repl_objset.c 13 KB

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