* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
- *
+ *
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
struct event_list **activequeues;
int nactivequeues;
+ /** The event whose callback is executing right now */
+ struct event *current_event;
+
/** Deferred callback management: a list of deferred callbacks to
* run active the active events. */
TAILQ_HEAD (deferred_cb_list, deferred_cb) deferred_cb_list;
unsigned long th_owner_id;
/** A lock to prevent conflicting accesses to this event_base */
void *th_base_lock;
+ /** A lock to prevent event_del from deleting an event while its
+ * callback is executing. */
+ void *current_event_lock;
#endif
#ifdef WIN32
if (!cfg || !(cfg->flags & EVENT_BASE_FLAG_NOLOCK)) {
int r;
EVTHREAD_ALLOC_LOCK(base->th_base_lock);
+ EVTHREAD_ALLOC_LOCK(base->current_event_lock);
r = evthread_make_base_notifiable(base);
if (r<0) {
event_base_free(base);
evmap_signal_clear(&base->sigmap);
EVTHREAD_FREE_LOCK(base->th_base_lock);
+ EVTHREAD_FREE_LOCK(base->current_event_lock);
mm_free(base);
}
ev->ev_res & EV_WRITE ? "EV_WRITE " : " ",
ev->ev_callback));
+ base->current_event = ev;
+
+ EVBASE_ACQUIRE_LOCK(base, EVTHREAD_WRITE, current_event_lock);
+
EVBASE_RELEASE_LOCK(base,
EVTHREAD_WRITE, th_base_lock);
break;
}
+ EVBASE_RELEASE_LOCK(base, EVTHREAD_WRITE, current_event_lock);
+ EVBASE_ACQUIRE_LOCK(base, EVTHREAD_WRITE, th_base_lock);
+ base->current_event = NULL;
+
if (base->event_break)
return -1;
- EVBASE_ACQUIRE_LOCK(base, EVTHREAD_WRITE, th_base_lock);
}
return count;
}
activeq = base->activequeues[i];
c = event_process_active_single_queue(base, activeq);
if (c < 0)
- return; /* already unlocked */
+ goto unlock;
else if (c > 0)
break; /* Processed a real event; do not
* consider lower-priority events */
event_process_deferred_callbacks(base);
+unlock:
EVBASE_RELEASE_LOCK(base, EVTHREAD_WRITE, th_base_lock);
}
return (res);
}
+/* Helper for event_del: always called with th_base_lock held. */
static inline int
event_del_internal(struct event *ev)
{
struct event_base *base;
int res = 0;
+ int need_cur_lock;
event_debug(("event_del: %p, callback %p",
ev, ev->ev_callback));
if (ev->ev_base == NULL)
return (-1);
+ /* If the main thread is currently executing this event's callback,
+ * and we are not the main thread, then we want to wait until the
+ * callback is done before we start removing the event. That way,
+ * when this function returns, it will be safe to free the
+ * user-supplied argument. */
base = ev->ev_base;
+ need_cur_lock = (base->current_event == ev);
+ if (need_cur_lock)
+ EVBASE_ACQUIRE_LOCK(base, EVTHREAD_WRITE, current_event_lock);
assert(!(ev->ev_flags & ~EVLIST_ALL));
if (res != -1 && !EVBASE_IN_THREAD(base))
evthread_notify_base(base);
+ if (need_cur_lock)
+ EVBASE_RELEASE_LOCK(base, EVTHREAD_WRITE, current_event_lock);
+
return (res);
}