]>
Commit | Line | Data |
---|---|---|
904e0804 TG |
1 | /* |
2 | * Copyright (c) 2013, 2014 Nicira, Inc. | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at: | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #ifndef OPENVSWITCH_THREAD_H | |
18 | #define OPENVSWITCH_THREAD_H 1 | |
19 | ||
20 | #include <pthread.h> | |
21 | #include <stddef.h> | |
22 | #include <stdbool.h> | |
23 | #include <sys/types.h> | |
3e3bed0b | 24 | #include "openvswitch/compiler.h" |
904e0804 TG |
25 | |
26 | /* Mutex. */ | |
27 | struct OVS_LOCKABLE ovs_mutex { | |
28 | pthread_mutex_t lock; | |
29 | const char *where; /* NULL if and only if uninitialized. */ | |
30 | }; | |
31 | ||
32 | /* "struct ovs_mutex" initializer. */ | |
33 | #ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP | |
34 | #define OVS_MUTEX_INITIALIZER { PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, \ | |
35 | "<unlocked>" } | |
36 | #else | |
37 | #define OVS_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER, "<unlocked>" } | |
38 | #endif | |
39 | ||
40 | #ifdef PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP | |
41 | #define OVS_ADAPTIVE_MUTEX_INITIALIZER \ | |
42 | { PTHREAD_ADAPTIVE_MUTEX_INITIALIZER_NP, "<unlocked>" } | |
43 | #else | |
44 | #define OVS_ADAPTIVE_MUTEX_INITIALIZER OVS_MUTEX_INITIALIZER | |
45 | #endif | |
46 | ||
47 | /* ovs_mutex functions analogous to pthread_mutex_*() functions. | |
48 | * | |
49 | * Most of these functions abort the process with an error message on any | |
50 | * error. ovs_mutex_trylock() is an exception: it passes through a 0 or EBUSY | |
51 | * return value to the caller and aborts on any other error. */ | |
52 | void ovs_mutex_init(const struct ovs_mutex *); | |
53 | void ovs_mutex_init_recursive(const struct ovs_mutex *); | |
54 | void ovs_mutex_init_adaptive(const struct ovs_mutex *); | |
55 | void ovs_mutex_destroy(const struct ovs_mutex *); | |
56 | void ovs_mutex_unlock(const struct ovs_mutex *mutex) OVS_RELEASES(mutex); | |
57 | void ovs_mutex_lock_at(const struct ovs_mutex *mutex, const char *where) | |
58 | OVS_ACQUIRES(mutex); | |
59 | #define ovs_mutex_lock(mutex) \ | |
60 | ovs_mutex_lock_at(mutex, OVS_SOURCE_LOCATOR) | |
61 | ||
62 | int ovs_mutex_trylock_at(const struct ovs_mutex *mutex, const char *where) | |
63 | OVS_TRY_LOCK(0, mutex); | |
64 | #define ovs_mutex_trylock(mutex) \ | |
65 | ovs_mutex_trylock_at(mutex, OVS_SOURCE_LOCATOR) | |
66 | ||
67 | void ovs_mutex_cond_wait(pthread_cond_t *, const struct ovs_mutex *); | |
68 | \f | |
69 | /* Convenient once-only execution. | |
70 | * | |
71 | * | |
72 | * Problem | |
73 | * ======= | |
74 | * | |
75 | * POSIX provides pthread_once_t and pthread_once() as primitives for running a | |
76 | * set of code only once per process execution. They are used like this: | |
77 | * | |
78 | * static void run_once(void) { ...initialization... } | |
79 | * static pthread_once_t once = PTHREAD_ONCE_INIT; | |
80 | * ... | |
81 | * pthread_once(&once, run_once); | |
82 | * | |
83 | * pthread_once() does not allow passing any parameters to the initialization | |
84 | * function, which is often inconvenient, because it means that the function | |
85 | * can only access data declared at file scope. | |
86 | * | |
87 | * | |
88 | * Solution | |
89 | * ======== | |
90 | * | |
91 | * Use ovsthread_once, like this, instead: | |
92 | * | |
93 | * static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER; | |
94 | * | |
95 | * if (ovsthread_once_start(&once)) { | |
96 | * ...initialization... | |
97 | * ovsthread_once_done(&once); | |
98 | * } | |
99 | */ | |
100 | ||
101 | struct ovsthread_once { | |
102 | bool done; /* Non-atomic, false negatives possible. */ | |
103 | struct ovs_mutex mutex; | |
104 | }; | |
105 | ||
106 | #define OVSTHREAD_ONCE_INITIALIZER \ | |
107 | { \ | |
108 | false, \ | |
109 | OVS_MUTEX_INITIALIZER, \ | |
110 | } | |
111 | ||
112 | static inline bool ovsthread_once_start(struct ovsthread_once *once) | |
113 | OVS_TRY_LOCK(true, once->mutex); | |
114 | void ovsthread_once_done(struct ovsthread_once *once) | |
115 | OVS_RELEASES(once->mutex); | |
116 | ||
117 | bool ovsthread_once_start__(struct ovsthread_once *once) | |
118 | OVS_TRY_LOCK(true, once->mutex); | |
119 | ||
120 | /* Returns true if this is the first call to ovsthread_once_start() for | |
121 | * 'once'. In this case, the caller should perform whatever initialization | |
122 | * actions it needs to do, then call ovsthread_once_done() for 'once'. | |
123 | * | |
124 | * Returns false if this is not the first call to ovsthread_once_start() for | |
125 | * 'once'. In this case, the call will not return until after | |
126 | * ovsthread_once_done() has been called. */ | |
127 | static inline bool | |
128 | ovsthread_once_start(struct ovsthread_once *once) | |
129 | { | |
130 | /* We may be reading 'done' at the same time as the first thread | |
131 | * is writing on it, or we can be using a stale copy of it. The | |
132 | * worst that can happen is that we call ovsthread_once_start__() | |
133 | * once when strictly not necessary. */ | |
134 | return OVS_UNLIKELY(!once->done && ovsthread_once_start__(once)); | |
135 | } | |
136 | ||
137 | #endif /* ovs-thread.h */ |