Define a new 'interlocked' status of netmgr - if it's active
that means there's an operation that depends on many workers. No two such operation can be launched at once without risking a deadlock, so other ones must either wait or requeue.
This commit is contained in:
@@ -234,6 +234,13 @@ struct isc_nm {
|
||||
atomic_uint_fast32_t workers_paused;
|
||||
atomic_uint_fast32_t maxudp;
|
||||
atomic_bool paused;
|
||||
/*
|
||||
* A worker is actively waiting for other workers, to for example
|
||||
* stop listening, that means no other thread can do the same thing
|
||||
* or pause or we'll deadlock. We have to either re-enqueue our event
|
||||
* or wait for the finish if we want to pause.
|
||||
*/
|
||||
atomic_bool interlocked;
|
||||
};
|
||||
|
||||
typedef enum isc_nmsocket_type {
|
||||
@@ -516,3 +523,12 @@ isc___nm_uverr2result(int uverr, bool dolog,
|
||||
* of this function should add any expected errors that are
|
||||
* not already there.
|
||||
*/
|
||||
|
||||
bool
|
||||
isc__nm_acquire_interlocked(isc_nm_t *mgr);
|
||||
|
||||
void
|
||||
isc__nm_drop_interlocked(isc_nm_t *mgr);
|
||||
|
||||
void
|
||||
isc__nm_acquire_interlocked_force(isc_nm_t *mgr);
|
||||
|
||||
@@ -186,6 +186,7 @@ isc_nm_pause(isc_nm_t *mgr) {
|
||||
REQUIRE(!isc__nm_in_netthread());
|
||||
|
||||
atomic_store(&mgr->paused, true);
|
||||
isc__nm_acquire_interlocked_force(mgr);
|
||||
|
||||
for (size_t i = 0; i < mgr->nworkers; i++) {
|
||||
isc__netievent_t *event = NULL;
|
||||
@@ -222,6 +223,7 @@ isc_nm_resume(isc_nm_t *mgr) {
|
||||
SIGNAL(&mgr->workers[i].cond);
|
||||
UNLOCK(&mgr->workers[i].lock);
|
||||
}
|
||||
isc__nm_drop_interlocked(mgr);
|
||||
|
||||
/*
|
||||
* We're not waiting for all the workers to come back to life;
|
||||
@@ -965,3 +967,38 @@ isc_nm_send(isc_nmhandle_t *handle, isc_region_t *region,
|
||||
ISC_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to acquire interlocked state - true if successful.
|
||||
*/
|
||||
bool
|
||||
isc__nm_acquire_interlocked(isc_nm_t *mgr) {
|
||||
LOCK(&mgr->lock);
|
||||
bool success = atomic_compare_exchange_strong(&mgr->interlocked, &(bool){false}, true);
|
||||
UNLOCK(&mgr->lock);
|
||||
return (success);
|
||||
}
|
||||
|
||||
/*
|
||||
* Lose interlocked state, signal waiters
|
||||
*/
|
||||
void
|
||||
isc__nm_drop_interlocked(isc_nm_t *mgr) {
|
||||
LOCK(&mgr->lock);
|
||||
bool success = atomic_compare_exchange_strong(&mgr->interlocked, &(bool){true}, false);
|
||||
INSIST(success == true);
|
||||
BROADCAST(&mgr->wkstatecond);
|
||||
UNLOCK(&mgr->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
* Actively wait for interlocked state
|
||||
*/
|
||||
void
|
||||
isc__nm_acquire_interlocked_force(isc_nm_t *mgr) {
|
||||
LOCK(&mgr->lock);
|
||||
while (!atomic_compare_exchange_strong(&mgr->interlocked, &(bool){false}, true)) {
|
||||
WAIT(&mgr->wkstatecond, &mgr->lock);
|
||||
}
|
||||
UNLOCK(&mgr->lock);
|
||||
}
|
||||
|
||||
+17
-24
@@ -174,20 +174,6 @@ stoplistening(isc_nmsocket_t *sock) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a child socket; stop listening and return.
|
||||
*/
|
||||
if (sock->parent != NULL) {
|
||||
stop_udp_child(sock);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* ... but if it's a parent socket, we need to send stop-
|
||||
* listening events to all the children first, and wait for
|
||||
* them to be executed.
|
||||
*/
|
||||
|
||||
INSIST(sock->type == isc_nm_udplistener);
|
||||
|
||||
for (int i = 0; i < sock->nchildren; i++) {
|
||||
@@ -224,18 +210,18 @@ isc_nm_udp_stoplistening(isc_nmsocket_t *sock) {
|
||||
REQUIRE(sock->type == isc_nm_udplistener);
|
||||
|
||||
/*
|
||||
* If the manager is paused, re-enqueue this as an asynchronous
|
||||
* If the manager is interlocked, re-enqueue this as an asynchronous
|
||||
* event. Otherwise, go ahead and stop listening right away.
|
||||
*/
|
||||
if (isc_nm_paused(sock->mgr)) {
|
||||
if (!isc__nm_acquire_interlocked(sock->mgr)) {
|
||||
ievent = isc__nm_get_ievent(sock->mgr, netievent_udpstoplisten);
|
||||
ievent->sock = sock;
|
||||
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
|
||||
(isc__netievent_t *) ievent);
|
||||
return;
|
||||
} else {
|
||||
stoplistening(sock);
|
||||
isc__nm_drop_interlocked(sock->mgr);
|
||||
}
|
||||
|
||||
stoplistening(sock);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -250,23 +236,30 @@ isc__nm_async_udpstoplisten(isc__networker_t *worker,
|
||||
isc_nmsocket_t *sock = ievent->sock;
|
||||
|
||||
REQUIRE(sock->iface != NULL);
|
||||
|
||||
UNUSED(worker);
|
||||
|
||||
/*
|
||||
* If this is a child socket; stop listening and return.
|
||||
*/
|
||||
if (sock->parent != NULL) {
|
||||
stop_udp_child(sock);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The network manager is pausing; re-enqueue this event for later.
|
||||
*/
|
||||
if (isc_nm_paused(sock->mgr)) {
|
||||
if (!isc__nm_acquire_interlocked(sock->mgr)) {
|
||||
isc__netievent_udplisten_t *event = NULL;
|
||||
|
||||
event = isc__nm_get_ievent(sock->mgr, netievent_udpstoplisten);
|
||||
event->sock = sock;
|
||||
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
|
||||
(isc__netievent_t *) event);
|
||||
return;
|
||||
} else {
|
||||
stoplistening(sock);
|
||||
isc__nm_drop_interlocked(sock->mgr);
|
||||
}
|
||||
|
||||
stoplistening(sock);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user