mirror of https://github.com/c-ares/c-ares.git
Basic Thread Safety (#636)
c-ares does not have any concept of thread-safety. It has always been 100% up to the implementor to ensure they never call c-ares from more than one thread at a time. This patch adds basic thread-safety support, which can be disabled at compile time if not desired. It uses a single recursive mutex per channel, which should be extremely quick when uncontested so overhead should be minimal. Fixes Bug: #610 Also sets the stage to implement #611 Fix By: Brad House (@bradh352)pull/641/head
parent
f1bf69c2d7
commit
a9442bd828
37 changed files with 605 additions and 48 deletions
@ -0,0 +1,43 @@ |
||||
.\" |
||||
.\" SPDX-License-Identifier: MIT |
||||
.\" |
||||
.TH ARES_REINIT 3 "26 November 2023" |
||||
.SH NAME |
||||
ares_threadsafety \- Query if c-ares was built with thread-safety |
||||
.SH SYNOPSIS |
||||
.nf |
||||
#include <ares.h> |
||||
|
||||
ares_bool_t ares_threadsafety(void); |
||||
.fi |
||||
.SH DESCRIPTION |
||||
The \fBares_threadsafety(3)\fP function returns if the library was built with |
||||
thread safety enabled or not. |
||||
|
||||
As of c-ares 1.23.0, this simply means that every public function which |
||||
references an \fIares_channel_t\fP object will lock the channel on entry and |
||||
release the lock on exit of the function. This will simply prevent concurrent |
||||
thread access to the channel, thus ensuring no corruption can occur. Future |
||||
versions will likely implement more threading-specific features. |
||||
|
||||
.SH RETURN VALUES |
||||
\fIares_threadsafety(3)\fP can return any of the following values: |
||||
.TP 14 |
||||
.B ARES_TRUE |
||||
Built with thread safety. |
||||
.TP 14 |
||||
.B ARES_FALSE |
||||
Built without thread safety |
||||
.TP 14 |
||||
|
||||
.SH AVAILABILITY |
||||
This function was first introduced in c-ares version 1.23.0. |
||||
.SH SEE ALSO |
||||
.BR ares_init (3), |
||||
.BR ares_init_options (3), |
||||
.BR ares_destroy (3), |
||||
.BR ares_dup (3), |
||||
.BR ares_library_init (3), |
||||
.BR ares_set_servers (3) |
||||
.SH AUTHOR |
||||
Copyright (C) 2023 The c-ares project and its members. |
@ -0,0 +1,184 @@ |
||||
/* MIT License
|
||||
* |
||||
* Copyright (c) 2023 Brad House |
||||
* |
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy |
||||
* of this software and associated documentation files (the "Software"), to deal |
||||
* in the Software without restriction, including without limitation the rights |
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||
* copies of the Software, and to permit persons to whom the Software is |
||||
* furnished to do so, subject to the following conditions: |
||||
* |
||||
* The above copyright notice and this permission notice (including the next |
||||
* paragraph) shall be included in all copies or substantial portions of the |
||||
* Software. |
||||
* |
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||
* SOFTWARE. |
||||
* |
||||
* SPDX-License-Identifier: MIT |
||||
*/ |
||||
#include "ares_setup.h" |
||||
#include "ares.h" |
||||
#include "ares_private.h" |
||||
|
||||
#ifdef CARES_THREADS |
||||
# ifdef _WIN32 |
||||
|
||||
struct ares__thread_mutex { |
||||
CRITICAL_SECTION mutex; |
||||
}; |
||||
|
||||
static ares__thread_mutex_t *ares__thread_mutex_create(void) |
||||
{ |
||||
ares__thread_mutex_t *mut = ares_malloc_zero(sizeof(*mut)); |
||||
if (mut == NULL) |
||||
return NULL; |
||||
|
||||
InitializeCriticalSection(&mut->mutex); |
||||
return mut; |
||||
} |
||||
|
||||
static void ares__thread_mutex_destroy(ares__thread_mutex_t *mut) |
||||
{ |
||||
if (mut == NULL) |
||||
return; |
||||
DeleteCriticalSection(&mut->mutex); |
||||
ares_free(mut); |
||||
} |
||||
|
||||
static void ares__thread_mutex_lock(ares__thread_mutex_t *mut) |
||||
{ |
||||
if (mut == NULL) |
||||
return; |
||||
EnterCriticalSection(&mut->mutex); |
||||
} |
||||
|
||||
static void ares__thread_mutex_unlock(ares__thread_mutex_t *mut) |
||||
{ |
||||
if (mut == NULL) |
||||
return; |
||||
LeaveCriticalSection(&mut->mutex); |
||||
} |
||||
|
||||
# else |
||||
# include <pthread.h> |
||||
|
||||
struct ares__thread_mutex { |
||||
pthread_mutex_t mutex; |
||||
}; |
||||
|
||||
static ares__thread_mutex_t *ares__thread_mutex_create(void) |
||||
{ |
||||
pthread_mutexattr_t attr; |
||||
ares__thread_mutex_t *mut = ares_malloc_zero(sizeof(*mut)); |
||||
if (mut == NULL) |
||||
return NULL; |
||||
|
||||
if (pthread_mutexattr_init(&attr) != 0) { |
||||
ares_free(mut); |
||||
return NULL; |
||||
} |
||||
|
||||
if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0) { |
||||
goto fail; |
||||
} |
||||
|
||||
if (pthread_mutex_init(&mut->mutex, &attr) != 0) { |
||||
goto fail; |
||||
} |
||||
|
||||
pthread_mutexattr_destroy(&attr); |
||||
return mut; |
||||
|
||||
fail: |
||||
pthread_mutexattr_destroy(&attr); |
||||
ares_free(mut); |
||||
return NULL; |
||||
} |
||||
|
||||
static void ares__thread_mutex_destroy(ares__thread_mutex_t *mut) |
||||
{ |
||||
if (mut == NULL) |
||||
return; |
||||
pthread_mutex_destroy(&mut->mutex); |
||||
ares_free(mut); |
||||
} |
||||
|
||||
static void ares__thread_mutex_lock(ares__thread_mutex_t *mut) |
||||
{ |
||||
if (mut == NULL) |
||||
return; |
||||
pthread_mutex_lock(&mut->mutex); |
||||
} |
||||
|
||||
static void ares__thread_mutex_unlock(ares__thread_mutex_t *mut) |
||||
{ |
||||
if (mut == NULL) |
||||
return; |
||||
pthread_mutex_unlock(&mut->mutex); |
||||
} |
||||
#endif |
||||
|
||||
ares_status_t ares__channel_threading_init(ares_channel_t *channel) |
||||
{ |
||||
channel->lock = ares__thread_mutex_create(); |
||||
if (channel->lock == NULL) |
||||
return ARES_ENOMEM; |
||||
return ARES_SUCCESS; |
||||
} |
||||
|
||||
void ares__channel_threading_destroy(ares_channel_t *channel) |
||||
{ |
||||
ares__thread_mutex_destroy(channel->lock); |
||||
channel->lock = NULL; |
||||
} |
||||
|
||||
void ares__channel_lock(ares_channel_t *channel) |
||||
{ |
||||
ares__thread_mutex_lock(channel->lock); |
||||
} |
||||
|
||||
void ares__channel_unlock(ares_channel_t *channel) |
||||
{ |
||||
ares__thread_mutex_unlock(channel->lock); |
||||
} |
||||
|
||||
ares_status_t ares_threadsafety(void) |
||||
{ |
||||
return ARES_TRUE; |
||||
} |
||||
|
||||
#else |
||||
/* NoOp */ |
||||
ares_status_t ares__channel_threading_init(ares_channel_t *channel) |
||||
{ |
||||
(void)channel; |
||||
return ARES_SUCCESS; |
||||
} |
||||
|
||||
void ares__channel_threading_destroy(ares_channel_t *channel) |
||||
{ |
||||
(void)channel; |
||||
} |
||||
|
||||
void ares__channel_lock(ares_channel_t *channel) |
||||
{ |
||||
(void)channel; |
||||
} |
||||
|
||||
void ares__channel_unlock(ares_channel_t *channel) |
||||
{ |
||||
(void)channel; |
||||
} |
||||
|
||||
ares_status_t ares_threadsafety(void) |
||||
{ |
||||
return ARES_FALSE; |
||||
} |
||||
#endif |
Loading…
Reference in new issue