repl_objset.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558
  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. co = llistGetFirst((*o)->objects, &cookie);
  161. while (NULL != co)
  162. {
  163. panic_fn(co->data);
  164. co = llistGetNext((*o)->objects, &cookie);
  165. }
  166. PR_Unlock((*o)->lock);
  167. }
  168. }
  169. else
  170. {
  171. /* Free the linked list */
  172. llistDestroy(&(*o)->objects, (*o)->destructor);
  173. PR_DestroyLock((*o)->lock);
  174. slapi_ch_free((void **)o);
  175. }
  176. }
  177. /*
  178. * Add an object to an object set.
  179. *
  180. * Arguments:
  181. * o: The object set to which the object is to be added.
  182. * name: a null-terminated string that names the object. Must
  183. * be unique.
  184. * obj: pointer to the object to be added.
  185. *
  186. * Return codes:
  187. * REPL_OBJSET_SUCCESS: the item was added to the object set
  188. * REPL_OBJSET_DUPLICATE_KEY: an item with the same key is already
  189. * in the object set.
  190. * REPL_OBJSET_INTERNAL_ERROR: something bad happened.
  191. */
  192. int
  193. repl_objset_add(Repl_Objset *o, const char *name, void *obj)
  194. {
  195. Repl_Objset_object *co = NULL;
  196. Repl_Objset_object *tmp = NULL;
  197. int rc = REPL_OBJSET_SUCCESS;
  198. PR_ASSERT(NULL != o);
  199. PR_ASSERT(NULL != name);
  200. PR_ASSERT(NULL != obj);
  201. PR_Lock(o->lock);
  202. tmp = llistGet(o->objects, name);
  203. if (NULL != tmp)
  204. {
  205. rc = REPL_OBJSET_DUPLICATE_KEY;
  206. goto loser;
  207. }
  208. co = (Repl_Objset_object *)slapi_ch_malloc(sizeof(Repl_Objset_object));
  209. co->data = obj;
  210. co->key = slapi_ch_strdup(name);
  211. co->refcnt = 0;
  212. co->flags = 0UL;
  213. if (llistInsertHead(o->objects, name, co) != 0)
  214. {
  215. rc = REPL_OBJSET_INTERNAL_ERROR;
  216. goto loser;
  217. }
  218. PR_Unlock(o->lock);
  219. return rc;
  220. loser:
  221. PR_Unlock(o->lock);
  222. if (NULL != co)
  223. {
  224. if (NULL != co->key)
  225. {
  226. slapi_ch_free((void **)&co->key);
  227. }
  228. slapi_ch_free((void **)&co);
  229. }
  230. return rc;
  231. }
  232. /* Must be called with the repl_objset locked */
  233. static void
  234. removeObjectNolock(Repl_Objset *o, Repl_Objset_object *co)
  235. {
  236. /* Remove from list */
  237. llistRemove(o->objects, co->key);
  238. /* Destroy the object */
  239. o->destructor(&(co->data));
  240. slapi_ch_free((void **)&(co->key));
  241. /* Deallocate the container */
  242. slapi_ch_free((void **)&co);
  243. }
  244. static Repl_Objset_object *
  245. removeCurrentObjectAndGetNextNolock (Repl_Objset *o, Repl_Objset_object *co, void *iterator)
  246. {
  247. Repl_Objset_object *ro;
  248. PR_ASSERT (o);
  249. PR_ASSERT (co);
  250. PR_ASSERT (iterator);
  251. ro = llistRemoveCurrentAndGetNext (o->objects, &iterator);
  252. o->destructor(&(co->data));
  253. slapi_ch_free((void **)&(co->key));
  254. /* Deallocate the container */
  255. slapi_ch_free((void **)&co);
  256. return ro;
  257. }
  258. /* Must be called with the repl_objset locked */
  259. static void
  260. acquireNoLock(Repl_Objset_object *co)
  261. {
  262. co->refcnt++;
  263. }
  264. /* Must be called with the repl_objset locked */
  265. static void
  266. releaseNoLock(Repl_Objset *o, Repl_Objset_object *co)
  267. {
  268. PR_ASSERT(co->refcnt >= 1);
  269. if (--co->refcnt == 0)
  270. {
  271. if (co->flags & REPL_OBJSET_OBJ_FLAG_DELETED)
  272. {
  273. /* Remove the object */
  274. removeObjectNolock(o, co);
  275. }
  276. }
  277. }
  278. /*
  279. * Retrieve an object from the object set. If an object with
  280. * the given key is found, its reference count is incremented,
  281. * a pointer to the object is returned, and a handle to use
  282. * to refer to the object is returned.
  283. *
  284. * Arguments:
  285. * o: The object set to be searched.
  286. * key: key of the object to be retrieved
  287. * obj: pointer to void * that will be set to point to the
  288. * object, if found.
  289. * handle: pointer to void * that will be set to point to a
  290. * handle, used to refer to the object, if found.
  291. *
  292. * Returns:
  293. * REPL_OBJSET_SUCCESS: an item was found.
  294. * REPL_OBJSET_KEY_NOT_FOUND: no item with the given key was found.
  295. */
  296. int
  297. repl_objset_acquire(Repl_Objset *o, const char *key, void **obj, void **handle)
  298. {
  299. Repl_Objset_object *co = NULL;
  300. int rc = REPL_OBJSET_KEY_NOT_FOUND;
  301. PR_ASSERT(NULL != o);
  302. PR_ASSERT(NULL != key);
  303. PR_ASSERT(NULL != obj);
  304. PR_ASSERT(NULL != handle);
  305. PR_Lock(o->lock);
  306. co = llistGet(o->objects, key);
  307. if (NULL != co && !(co->flags & REPL_OBJSET_OBJ_FLAG_DELETED))
  308. {
  309. acquireNoLock(co);
  310. *obj = (void *)co->data;
  311. *handle = (void *)co;
  312. rc = REPL_OBJSET_SUCCESS;
  313. }
  314. PR_Unlock(o->lock);
  315. return rc;
  316. }
  317. /*
  318. * Return an object to the object set.
  319. *
  320. * Arguments:
  321. * o: The object set containing the objct
  322. * handle: reference to the object.
  323. *
  324. */
  325. void
  326. repl_objset_release(Repl_Objset *o, void *handle)
  327. {
  328. Repl_Objset_object *co;
  329. PR_ASSERT(NULL != o);
  330. PR_ASSERT(NULL != handle);
  331. co = (Repl_Objset_object *)handle;
  332. PR_Lock(o->lock);
  333. releaseNoLock(o, co);
  334. PR_Unlock(o->lock);
  335. }
  336. /*
  337. * Delete an object from the object set
  338. *
  339. * Arguments:
  340. * o: The object set containing the object.
  341. * handle: reference to the object.
  342. */
  343. void
  344. repl_objset_delete(Repl_Objset *o, void *handle)
  345. {
  346. Repl_Objset_object *co = (Repl_Objset_object *)handle;
  347. PR_ASSERT(NULL != o);
  348. PR_ASSERT(NULL != co);
  349. PR_Lock(o->lock);
  350. if (co->refcnt == 0)
  351. {
  352. removeObjectNolock(o, co);
  353. }
  354. else
  355. {
  356. /* Set deleted flag, clean up later */
  357. co->flags |= REPL_OBJSET_OBJ_FLAG_DELETED;
  358. }
  359. PR_Unlock(o->lock);
  360. }
  361. typedef struct _iterator
  362. {
  363. Repl_Objset *o; /* set for which iterator was created */
  364. void *cookie; /* for linked list */
  365. Repl_Objset_object *co; /* our wrapper */
  366. } iterator;
  367. /*
  368. * Get the first object in an object set.
  369. * Used when enumerating all of the objects in a set.
  370. * Arguments:
  371. * o: The object set being enumerated
  372. * itcontext: an iteration context, to be passed back to subsequent calls
  373. * to repl_objset_next_object.
  374. * handle: a pointer to pointer to void. This will be filled in with
  375. * a reference to the object's enclosing object.
  376. * Returns:
  377. * A pointer to the next object in the set, or NULL if there are no
  378. * objects in the set.
  379. *
  380. */
  381. void *
  382. repl_objset_first_object(Repl_Objset *o, void **itcontext, void **handle)
  383. {
  384. Repl_Objset_object *co = NULL;
  385. void *cookie;
  386. void *retptr = NULL;
  387. iterator *it;
  388. PR_ASSERT(NULL != o);
  389. PR_ASSERT(NULL != itcontext);
  390. *itcontext = NULL;
  391. if (NULL == o->objects) {
  392. return(NULL);
  393. }
  394. /* Find the first non-deleted object */
  395. PR_Lock(o->lock);
  396. co = llistGetFirst(o->objects, &cookie);
  397. while (NULL != co && (co->flags & REPL_OBJSET_OBJ_FLAG_DELETED))
  398. {
  399. co = llistGetNext(o->objects, &cookie);
  400. }
  401. if (NULL != co)
  402. {
  403. /* Increment refcnt until item given back to us */
  404. acquireNoLock(co);
  405. /* Save away context */
  406. it = (iterator *)slapi_ch_malloc(sizeof(iterator));
  407. *itcontext = it;
  408. it->o = o;
  409. it->cookie = cookie;
  410. it->co = co;
  411. retptr = co->data;
  412. }
  413. PR_Unlock(o->lock);
  414. if (NULL != handle)
  415. {
  416. *handle = co;
  417. }
  418. return retptr;
  419. }
  420. /*
  421. * Get the next object in the set.
  422. * Arguments:
  423. * o: The object set being enumerated
  424. * itcontext: an iteration context, to be passed back to subsequent calls
  425. * to repl_objset_next_object.
  426. * handle: a pointer to pointer to void. This will be filled in with
  427. * a reference to the object's enclosing object.
  428. *
  429. * Returns:
  430. * A pointer to the next object in the set, or NULL if there are no more
  431. * objects.
  432. */
  433. void *
  434. repl_objset_next_object(Repl_Objset *o, void *itcontext, void **handle)
  435. {
  436. Repl_Objset_object *co = NULL;
  437. Repl_Objset_object *tmp_co;
  438. void *retptr = NULL;
  439. iterator *it = (iterator *)itcontext;
  440. PR_ASSERT(NULL != o);
  441. PR_ASSERT(NULL != it);
  442. PR_ASSERT(NULL != it->co);
  443. PR_Lock(o->lock);
  444. tmp_co = it->co;
  445. /* Find the next non-deleted object */
  446. while ((co = llistGetNext(o->objects, &it->cookie)) != NULL &&
  447. !(co->flags & REPL_OBJSET_OBJ_FLAG_DELETED));
  448. if (NULL != co)
  449. {
  450. acquireNoLock(co);
  451. it->co = co;
  452. retptr = co->data;
  453. }
  454. else
  455. {
  456. /*
  457. * No more non-deleted objects - erase context (freeing
  458. * it is responsibility of caller.
  459. */
  460. it->cookie = NULL;
  461. it->co = NULL;
  462. }
  463. releaseNoLock(o, tmp_co);
  464. PR_Unlock(o->lock);
  465. if (NULL != handle)
  466. {
  467. *handle = co;
  468. }
  469. return retptr;
  470. }
  471. /*
  472. * Destroy an itcontext iterator
  473. */
  474. void
  475. repl_objset_iterator_destroy(void **itcontext)
  476. {
  477. if (NULL != itcontext && NULL != *itcontext)
  478. {
  479. /* check if we did not iterate through the entire list
  480. and need to release last accessed element */
  481. iterator *it = *(iterator**)itcontext;
  482. if (it->co)
  483. repl_objset_release (it->o, it->co);
  484. slapi_ch_free((void **)itcontext);
  485. }
  486. }