-
Notifications
You must be signed in to change notification settings - Fork 148
/
tcmur_work.c
131 lines (105 loc) · 2.58 KB
/
tcmur_work.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
* Copyright (c) 2020 Red Hat, Inc.
*
* This file is licensed to you under your choice of the GNU Lesser
* General Public License, version 2.1 or any later version (LGPLv2.1 or
* later), or the Apache License 2.0.
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <pthread.h>
#include "libtcmu.h"
#include "libtcmu_log.h"
#include "tcmur_device.h"
#include "tcmur_work.h"
struct tcmur_work *tcmur_create_work(void)
{
struct tcmur_work *work;
work = calloc(1, sizeof(*work));
if (!work)
return NULL;
if (pthread_mutex_init(&work->lock, NULL))
goto free_work;
if (pthread_cond_init(&work->cond, NULL))
goto destroy_mutex;
return work;
destroy_mutex:
pthread_mutex_destroy(&work->lock);
free_work:
free(work);
return NULL;
}
static void __tcmur_flush_work(struct tcmur_work *work)
{
char pname[TCMU_THREAD_NAME_LEN];
if (pthread_getname_np(pthread_self(), pname, TCMU_THREAD_NAME_LEN))
return;
/*
* The event work thread may need to do a handler reopen
* call and try to flush itself. Just ignore.
*/
if (!strcmp(pname, "ework-thread"))
return;
/*
* Some handlers will crash if we do a cancel so we just wait.
*/
tcmu_dbg("waiting for %d work thread to complete\n", work->refcnt);
if (work->refcnt)
pthread_cond_wait(&work->cond, &work->lock);
}
void tcmur_flush_work(struct tcmur_work *work)
{
pthread_mutex_lock(&work->lock);
__tcmur_flush_work(work);
pthread_mutex_unlock(&work->lock);
}
struct private {
void *data;
void (*work_fn)(void *);
struct tcmur_work *work;
};
static void *tcmur_work_fn(void *data)
{
struct private *p = data;
tcmu_set_thread_name("ework-thread", NULL);
p->work_fn(p->data);
pthread_mutex_lock(&p->work->lock);
if (--p->work->refcnt == 0)
pthread_cond_signal(&p->work->cond);
pthread_mutex_unlock(&p->work->lock);
free(p);
return NULL;
}
int tcmur_run_work(struct tcmur_work *work, void *data, void (*work_fn)(void *))
{
pthread_attr_t attr;
pthread_t thread;
struct private *p;
int ret;
p = malloc(sizeof(struct private));
if (!p)
return -ENOMEM;
p->data = data;
p->work_fn = work_fn;
p->work = work;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_mutex_lock(&work->lock);
ret = pthread_create(&thread, &attr, tcmur_work_fn, p);
if (!ret)
work->refcnt++;
pthread_mutex_unlock(&work->lock);
pthread_attr_destroy(&attr);
if (ret)
free(p);
return ret;
}
void tcmur_destroy_work(struct tcmur_work *work)
{
tcmur_flush_work(work);
pthread_mutex_destroy(&work->lock);
pthread_cond_destroy(&work->cond);
free(work);
}