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