提交 e8dfb5bf 编写于 作者: M Matt Caswell

Add ASYNC_block_pause and ASYNC_unblock_pause

There are potential deadlock situations that can occur if code executing
within the context of a job aquires a lock, and then pauses the job. This
adds an ability to temporarily block pauses from occuring whilst performing
work and holding a lock.
Reviewed-by: NRich Salz <rsalz@openssl.org>
上级 f4511d48
......@@ -80,6 +80,7 @@ static async_ctx *async_ctx_new(void)
async_fibre_init_dispatcher(&nctx->dispatcher);
nctx->currjob = NULL;
nctx->blocked = 0;
if(!async_set_ctx(nctx))
goto err;
......@@ -286,7 +287,9 @@ int ASYNC_pause_job(void)
{
ASYNC_JOB *job;
if(!async_get_ctx() || !async_get_ctx()->currjob) {
if (async_get_ctx() == NULL
|| async_get_ctx()->currjob == NULL
|| async_get_ctx()->blocked) {
/*
* Could be we've deliberately not been started within a job so this is
* counted as success.
......@@ -297,8 +300,8 @@ int ASYNC_pause_job(void)
job = async_get_ctx()->currjob;
job->status = ASYNC_JOB_PAUSING;
if(!async_fibre_swapcontext(&job->fibrectx,
&async_get_ctx()->dispatcher, 1)) {
if (!async_fibre_swapcontext(&job->fibrectx,
&async_get_ctx()->dispatcher, 1)) {
ASYNCerr(ASYNC_F_ASYNC_PAUSE_JOB, ASYNC_R_FAILED_TO_SWAP_CONTEXT);
return 0;
}
......@@ -405,3 +408,28 @@ void ASYNC_clear_wake(ASYNC_JOB *job)
async_read1(job->wait_fd, &dummy);
job->wake_set = 0;
}
void ASYNC_block_pause(void)
{
if (async_get_ctx() == NULL
|| async_get_ctx()->currjob == NULL) {
/*
* We're not in a job anyway so ignore this
*/
return;
}
async_get_ctx()->blocked++;
}
void ASYNC_unblock_pause(void)
{
if (async_get_ctx() == NULL
|| async_get_ctx()->currjob == NULL) {
/*
* We're not in a job anyway so ignore this
*/
return;
}
if(async_get_ctx()->blocked > 0)
async_get_ctx()->blocked--;
}
......@@ -63,6 +63,7 @@ typedef struct async_ctx_st async_ctx;
struct async_ctx_st {
async_fibre dispatcher;
ASYNC_JOB *currjob;
unsigned int blocked;
};
struct async_job_st {
......
......@@ -4,7 +4,8 @@
ASYNC_init_pool, ASYNC_free_pool, ASYNC_start_job, ASYNC_pause_job,
ASYNC_in_job, ASYNC_get_wait_fd, ASYNC_get_current_job, ASYNC_wake,
ASYNC_clear_wake - asynchronous job management functions
ASYNC_clear_wake, ASYNC_block_pause, ASYNC_unblock_pause - asynchronous job
management functions
=head1 SYNOPSIS
......@@ -21,6 +22,8 @@ ASYNC_clear_wake - asynchronous job management functions
ASYNC_JOB *ASYNC_get_current_job(void);
void ASYNC_wake(ASYNC_JOB *job);
void ASYNC_clear_wake(ASYNC_JOB *job);
void ASYNC_block_pause(void);
void ASYNC_unblock_pause(void);
=head1 DESCRIPTION
......@@ -121,6 +124,20 @@ engine can signal to the user code that the job should be resumed using
ASYNC_wake(). Once resumed the engine would clear the wake signal by calling
ASYNC_clear_wake().
The ASYNC_block_pause() function will prevent the currently active job from
pausing. The block will remain in place until a subsequent call to
ASYNC_unblock_pause(). These functions can be nested, e.g. if you call
ASYNC_block_pause() twice then you must call ASYNC_unblock_pause() twice in
order to reenable pausing. If these functions are called while there is no
currently active job then they have no effect. This functionality can be useful
to avoid deadlock scenarios. For example during the execution of an ASYNC_JOB an
application aquires a lock. It then calls some cryptographic function which
invokes ASYNC_pause_job(). This returns control back to the code that created
the ASYNC_JOB. If that code then attempts to aquire the same lock before
resuming the original job then a deadlock can occur. By calling
ASYNC_block_pause() immediately after aquiring the lock and
ASYNC_unblock_pause() immediately before releasing it then this situation cannot
occur.
=head1 RETURN VALUES
......
......@@ -78,6 +78,8 @@ int ASYNC_get_wait_fd(ASYNC_JOB *job);
ASYNC_JOB *ASYNC_get_current_job(void);
void ASYNC_wake(ASYNC_JOB *job);
void ASYNC_clear_wake(ASYNC_JOB *job);
void ASYNC_block_pause(void);
void ASYNC_unblock_pause(void);
/* BEGIN ERROR CODES */
/*
......
......@@ -114,6 +114,16 @@ static int wake(void *args)
return 1;
}
static int blockpause(void *args)
{
ASYNC_block_pause();
ASYNC_pause_job();
ASYNC_unblock_pause();
ASYNC_pause_job();
return 1;
}
static int test_ASYNC_init_pool()
{
ASYNC_JOB *job1 = NULL, *job2 = NULL, *job3 = NULL;
......@@ -210,8 +220,6 @@ static int test_ASYNC_get_wait_fd()
ASYNC_JOB *job = NULL;
int funcret, fd;
currjob = NULL;
if ( !ASYNC_init_pool(1, 0)
|| ASYNC_start_job(&job, &funcret, wake, NULL, 0)
!= ASYNC_PAUSE
......@@ -235,6 +243,27 @@ static int test_ASYNC_get_wait_fd()
ASYNC_free_pool();
return 1;
}
static int test_ASYNC_block_pause()
{
ASYNC_JOB *job = NULL;
int funcret;
if ( !ASYNC_init_pool(1, 0)
|| ASYNC_start_job(&job, &funcret, blockpause, NULL, 0)
!= ASYNC_PAUSE
|| ASYNC_start_job(&job, &funcret, blockpause, NULL, 0)
!= ASYNC_FINISH
|| funcret != 1) {
fprintf(stderr, "test_ASYNC_block_pause() failed\n");
ASYNC_free_pool();
return 0;
}
ASYNC_free_pool();
return 1;
}
#endif
int main(int argc, char **argv)
......@@ -250,7 +279,8 @@ int main(int argc, char **argv)
if ( !test_ASYNC_init_pool()
|| !test_ASYNC_start_job()
|| !test_ASYNC_get_current_job()
|| !test_ASYNC_get_wait_fd()) {
|| !test_ASYNC_get_wait_fd()
|| !test_ASYNC_block_pause()) {
return 1;
}
#endif
......
......@@ -4659,3 +4659,5 @@ ASYNC_clear_wake 5018 EXIST::FUNCTION:
ASYNC_get_current_job 5019 EXIST::FUNCTION:
ASYNC_get_wait_fd 5020 EXIST::FUNCTION:
ERR_load_ASYNC_strings 5021 EXIST::FUNCTION:
ASYNC_unblock_pause 5022 EXIST::FUNCTION:
ASYNC_block_pause 5023 EXIST::FUNCTION:
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册