- /* unref the per-process callback list */
- if (atomic_dec_and_test(&cfg_child_cb->refcnt)) {
- /* No more pocess refers to this callback.
- Did this process block the deletion,
- or is there any other process that has not
- reached prev_cb yet? */
- CFG_LOCK();
- if (*cfg_child_cb_first == cfg_child_cb) {
- /* yes, this process was blocking the deletion */
- *cfg_child_cb_first = cfg_child_cb->next;
- CFG_UNLOCK();
- shm_free(cfg_child_cb);
+ if (!cfg_child_cb) return;
+
+ /* The lock must be held to make sure that the global config
+ is not replaced meantime, and the other child processes do not
+ leave the old value of *cfg_child_cb_last. Otherwise it could happen,
+ that all the other processes move their own cfg_child_cb pointer before
+ this process reaches *cfg_child_cb_last, though, it is very unlikely. */
+ CFG_LOCK();
+
+ /* go through the list and check whether there is any item that
+ has to be freed (similar to cfg_update_local(), but without executing
+ the callback functions) */
+ while (cfg_child_cb != *cfg_child_cb_last) {
+ prev_cb = cfg_child_cb;
+ cfg_child_cb = cfg_child_cb->next;
+ atomic_inc(&cfg_child_cb->refcnt);
+ if (atomic_dec_and_test(&prev_cb->refcnt)) {
+ /* No more pocess refers to this callback.
+ Did this process block the deletion,
+ or is there any other process that has not
+ reached prev_cb yet? */
+ if (*cfg_child_cb_first == prev_cb) {
+ /* yes, this process was blocking the deletion */
+ *cfg_child_cb_first = cfg_child_cb;
+ shm_free(prev_cb);
+ }