2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-26 20:17:35 +00:00
bind/lib/isc/managers.c
Ondřej Surý 6bd025942c Replace netievent lock-free queue with simple locked queue
The current implementation of isc_queue uses Michael-Scott lock-free
queue that in turn uses hazard pointers.  It was discovered that the way
we use the isc_queue, such complicated mechanism isn't really needed,
because most of the time, we either execute the work directly when on
nmthread (in case of UDP) or schedule the work from the matching
nmthreads.

Replace the current implementation of the isc_queue with a simple locked
ISC_LIST.  There's a slight improvement - since copying the whole list
is very lightweight - we move the queue into a new list before we start
the processing and locking just for moving the queue and not for every
single item on the list.

NOTE: There's a room for future improvements - since we don't guarantee
the order in which the netievents are processed, we could have two lists
- one unlocked that would be used when scheduling the work from the
matching thread and one locked that would be used from non-matching
thread.
2022-03-04 13:49:51 +01:00

120 lines
2.9 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <isc/managers.h>
#include <isc/util.h>
#include "netmgr_p.h"
#include "task_p.h"
#include "timer_p.h"
isc_result_t
isc_managers_create(isc_mem_t *mctx, size_t workers, size_t quantum,
isc_nm_t **netmgrp, isc_taskmgr_t **taskmgrp,
isc_timermgr_t **timermgrp) {
isc_result_t result;
isc_nm_t *netmgr = NULL;
isc_taskmgr_t *taskmgr = NULL;
isc_timermgr_t *timermgr = NULL;
REQUIRE(netmgrp != NULL && *netmgrp == NULL);
isc__netmgr_create(mctx, workers, &netmgr);
*netmgrp = netmgr;
INSIST(netmgr != NULL);
REQUIRE(taskmgrp == NULL || *taskmgrp == NULL);
if (taskmgrp != NULL) {
INSIST(netmgr != NULL);
result = isc__taskmgr_create(mctx, quantum, netmgr, &taskmgr);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_taskmgr_create() failed: %s",
isc_result_totext(result));
goto fail;
}
*taskmgrp = taskmgr;
}
REQUIRE(timermgrp == NULL || *timermgrp == NULL);
if (timermgrp != NULL) {
result = isc__timermgr_create(mctx, &timermgr);
if (result != ISC_R_SUCCESS) {
UNEXPECTED_ERROR(__FILE__, __LINE__,
"isc_timermgr_create() failed: %s",
isc_result_totext(result));
goto fail;
}
*timermgrp = timermgr;
}
return (ISC_R_SUCCESS);
fail:
isc_managers_destroy(netmgrp, taskmgrp, timermgrp);
return (result);
}
void
isc_managers_destroy(isc_nm_t **netmgrp, isc_taskmgr_t **taskmgrp,
isc_timermgr_t **timermgrp) {
/*
* If we have a taskmgr to clean up, then we must also have a netmgr.
*/
REQUIRE(taskmgrp == NULL || netmgrp != NULL);
/*
* The sequence of operations here is important:
*
* 1. Initiate shutdown of the taskmgr, sending shutdown events to
* all tasks that are not already shutting down.
*/
if (taskmgrp != NULL) {
INSIST(*taskmgrp != NULL);
isc__taskmgr_shutdown(*taskmgrp);
}
/*
* 2. Initiate shutdown of the network manager, freeing clients
* and other resources and preventing new connections, but do
* not stop processing of existing events.
*/
if (netmgrp != NULL) {
INSIST(*netmgrp != NULL);
isc__netmgr_shutdown(*netmgrp);
}
/*
* 3. Finish destruction of the task manager when all tasks
* have completed.
*/
if (taskmgrp != NULL) {
isc__taskmgr_destroy(taskmgrp);
}
/*
* 4. Finish destruction of the netmgr, and wait until all
* references have been released.
*/
if (netmgrp != NULL) {
isc__netmgr_destroy(netmgrp);
}
/*
* 5. Clean up the remaining managers.
*/
if (timermgrp != NULL) {
INSIST(*timermgrp != NULL);
isc__timermgr_destroy(timermgrp);
}
}