1.1 --- a/libfsclient/include/fsclient/client.h Sat Aug 14 18:41:57 2021 +0200
1.2 +++ b/libfsclient/include/fsclient/client.h Tue Aug 17 23:44:36 2021 +0200
1.3 @@ -72,8 +72,8 @@
1.4 /* Notification operations. */
1.5
1.6 long client_set_blocking(file_t *file, notify_flags_t flags);
1.7 -long client_subscribe(file_t *file, notify_flags_t flags);
1.8 -long client_unsubscribe(file_t *file);
1.9 +long client_subscribe(file_t *file, notify_flags_t flags, notifier_t notifier_type);
1.10 +long client_unsubscribe(file_t *file, notifier_t notifier_type);
1.11 long client_wait_file(file_t *file);
1.12 long client_wait_files(file_t **file);
1.13
2.1 --- a/libfsclient/include/fsclient/file.h Sat Aug 14 18:41:57 2021 +0200
2.2 +++ b/libfsclient/include/fsclient/file.h Tue Aug 17 23:44:36 2021 +0200
2.3 @@ -38,10 +38,6 @@
2.4
2.5 l4_cap_idx_t ref;
2.6
2.7 - /* Notification endpoint reference. */
2.8 -
2.9 - l4_cap_idx_t notifier;
2.10 -
2.11 /* Mapped memory accessing a file region. */
2.12
2.13 char *memory;
2.14 @@ -72,6 +68,17 @@
2.15
2.16
2.17
2.18 +/* Notifier types. */
2.19 +
2.20 +typedef enum
2.21 +{
2.22 + NOTIFIER_TASK = 0,
2.23 + NOTIFIER_THREAD = 1,
2.24 +
2.25 +} notifier_t;
2.26 +
2.27 +
2.28 +
2.29 /* Filesystem operations. */
2.30
2.31 long file_open_for_user(user_t user, l4_cap_idx_t server, l4_cap_idx_t *opener);
2.32 @@ -118,8 +125,8 @@
2.33
2.34 /* Notification functions. */
2.35
2.36 -long file_notify_subscribe(file_t *file, notify_flags_t flags);
2.37 -long file_notify_unsubscribe(file_t *file);
2.38 +long file_notify_subscribe(file_t *file, notify_flags_t flags, notifier_t notifier_type);
2.39 +long file_notify_unsubscribe(file_t *file, notifier_t notifier_type);
2.40 long file_notify_wait_file(file_t *file);
2.41 long file_notify_wait_files(file_t **file);
2.42
3.1 --- a/libfsclient/include/fsclient/notifier.h Sat Aug 14 18:41:57 2021 +0200
3.2 +++ b/libfsclient/include/fsclient/notifier.h Tue Aug 17 23:44:36 2021 +0200
3.3 @@ -27,14 +27,37 @@
3.4 #include <mutex>
3.5
3.6 #include <fsclient/file.h>
3.7 -#include <ipc/server.h>
3.8 #include <systypes/base.h>
3.9
3.10
3.11
3.12 +/* File-specific notification details. */
3.13 +
3.14 +class FileNotificationState
3.15 +{
3.16 +public:
3.17 + /* Synchronisation primitives for state access and notification. */
3.18 +
3.19 + std::mutex lock;
3.20 + std::condition_variable condition;
3.21 +
3.22 + /* Pending notifications for monitored files. */
3.23 +
3.24 + notify_flags_t pending = 0;
3.25 +
3.26 + /* Endpoints associated with monitored files. */
3.27 +
3.28 + l4_cap_idx_t endpoint = L4_INVALID_CAP;
3.29 +
3.30 + bool is_null() { return l4_is_invalid_cap(endpoint); }
3.31 +};
3.32 +
3.33 +
3.34 +
3.35 /* Collection types. */
3.36
3.37 -typedef std::map<file_t *, notify_flags_t> FileNotifications;
3.38 +typedef std::map<file_t *, FileNotificationState> FileNotificationStates;
3.39 +typedef std::map<file_t *, std::mutex> FileStateLocks;
3.40
3.41
3.42
3.43 @@ -43,28 +66,36 @@
3.44 class FileNotifier
3.45 {
3.46 protected:
3.47 - std::list<file_t *> _affected;
3.48 - FileNotifications _affected_flags;
3.49 + /* General state access locking. */
3.50 +
3.51 + std::mutex _state_lock;
3.52 +
3.53 + /* File-specific state locking. */
3.54 +
3.55 + FileStateLocks _file_locks;
3.56 +
3.57 + /* Notification state. */
3.58 +
3.59 + FileNotificationStates _state;
3.60 +
3.61 + /* Notifier thread details. */
3.62 +
3.63 l4_cap_idx_t _thread = L4_INVALID_CAP;
3.64 bool _started = false;
3.65
3.66 - std::mutex _lock;
3.67 - std::condition_variable _notified;
3.68 + /* Convenience method to access file state. */
3.69 +
3.70 + virtual FileNotificationState &file_state(file_t *file, bool create);
3.71
3.72 /* Helper methods. */
3.73
3.74 - virtual void _notify(file_t *file, notify_flags_t flags);
3.75 + virtual void _notify(file_t *file, notify_flags_t flags) = 0;
3.76 +
3.77 + virtual bool _transfer(FileNotificationState &state, file_t *file);
3.78 +
3.79 + virtual void _unsubscribe(FileNotificationState &state, file_t *file);
3.80
3.81 public:
3.82 - /* Server details. */
3.83 -
3.84 - int expected_items();
3.85 -
3.86 - ipc_server_handler_type handler();
3.87 -
3.88 - void *interface()
3.89 - { return static_cast<FileNotifier *>(this); }
3.90 -
3.91 /* Local operations. */
3.92
3.93 virtual long start();
3.94 @@ -73,10 +104,6 @@
3.95
3.96 virtual long unsubscribe(file_t *file);
3.97
3.98 - virtual long wait(file_t **file);
3.99 -
3.100 - virtual long wait_file(file_t *file);
3.101 -
3.102 /* Event handling support. */
3.103
3.104 virtual void mainloop();
3.105 @@ -84,8 +111,59 @@
3.106
3.107
3.108
3.109 +/* An object monitoring notifications for a collection of different files. */
3.110 +
3.111 +class GeneralFileNotifier : public FileNotifier
3.112 +{
3.113 +protected:
3.114 + /* Locking to protect pending notification members and to coordinate access
3.115 + to notifications. */
3.116 +
3.117 + std::mutex _general_lock;
3.118 +
3.119 + /* General lock synchronisation. */
3.120 +
3.121 + std::condition_variable _general_condition;
3.122 +
3.123 + /* Files affected by notifications. */
3.124 +
3.125 + std::list<file_t *> _affected;
3.126 +
3.127 + /* Helper methods. */
3.128 +
3.129 + virtual void _notify(file_t *file, notify_flags_t flags);
3.130 +
3.131 + virtual bool _retrieve(file_t **file);
3.132 +
3.133 + virtual bool _retrieve_for_file(file_t *file);
3.134 +
3.135 +public:
3.136 + virtual long wait(file_t **file);
3.137 +};
3.138 +
3.139 +
3.140 +
3.141 +/* An object monitoring notifications for specific files. */
3.142 +
3.143 +class SpecificFileNotifier : public FileNotifier
3.144 +{
3.145 +protected:
3.146 + /* Helper methods. */
3.147 +
3.148 + virtual void _notify(file_t *file, notify_flags_t flags);
3.149 +
3.150 +public:
3.151 + virtual long wait_file(file_t *file);
3.152 +};
3.153 +
3.154 +
3.155 +
3.156 /* Helper functions. */
3.157
3.158 -FileNotifier *get_notifier();
3.159 +FileNotifier *get_notifier(notifier_t notifier_type);
3.160 +
3.161 +SpecificFileNotifier *get_task_notifier();
3.162 +
3.163 +GeneralFileNotifier *get_thread_notifier();
3.164
3.165 // vim: tabstop=4 expandtab shiftwidth=4
4.1 --- a/libfsclient/lib/src/client.cc Sat Aug 14 18:41:57 2021 +0200
4.2 +++ b/libfsclient/lib/src/client.cc Tue Aug 17 23:44:36 2021 +0200
4.3 @@ -545,12 +545,13 @@
4.4 if (file->can_block == flags)
4.5 return L4_EOK;
4.6
4.7 - // NOTE: Set appropriate flags.
4.8 + /* Since blocking access is used with specific file notifications, the
4.9 + per-task notifier is used. */
4.10
4.11 if (flags)
4.12 - err = client_subscribe(file, flags);
4.13 + err = client_subscribe(file, flags, NOTIFIER_TASK);
4.14 else
4.15 - err = client_unsubscribe(file);
4.16 + err = client_unsubscribe(file, NOTIFIER_TASK);
4.17
4.18 if (err)
4.19 return err;
4.20 @@ -563,12 +564,12 @@
4.21
4.22 /* Subscribe from events concerning a file. */
4.23
4.24 -long client_subscribe(file_t *file, notify_flags_t flags)
4.25 +long client_subscribe(file_t *file, notify_flags_t flags, notifier_t notifier_type)
4.26 {
4.27 if (file == NULL)
4.28 return -L4_EINVAL;
4.29
4.30 - return file_notify_subscribe(file, flags);
4.31 + return file_notify_subscribe(file, flags, notifier_type);
4.32 }
4.33
4.34
4.35 @@ -587,12 +588,12 @@
4.36
4.37 /* Unsubscribe from events concerning a file. */
4.38
4.39 -long client_unsubscribe(file_t *file)
4.40 +long client_unsubscribe(file_t *file, notifier_t notifier_type)
4.41 {
4.42 if (file == NULL)
4.43 return -L4_EINVAL;
4.44
4.45 - return file_notify_unsubscribe(file);
4.46 + return file_notify_unsubscribe(file, notifier_type);
4.47 }
4.48
4.49
5.1 --- a/libfsclient/lib/src/file.cc Sat Aug 14 18:41:57 2021 +0200
5.2 +++ b/libfsclient/lib/src/file.cc Tue Aug 17 23:44:36 2021 +0200
5.3 @@ -79,7 +79,6 @@
5.4 {
5.5 file->memory = NULL;
5.6 file->ref = L4_INVALID_CAP;
5.7 - file->notifier = L4_INVALID_CAP;
5.8 file->start_pos = 0;
5.9 file->end_pos = 0;
5.10 file->data_end = 0;
5.11 @@ -98,8 +97,10 @@
5.12 if (l4_is_valid_cap(file->ref))
5.13 ipc_cap_free_um(file->ref);
5.14
5.15 - if (l4_is_valid_cap(file->notifier))
5.16 - ipc_cap_free_um(file->notifier);
5.17 + /* Only unsubscribe after actually closing the file and sending any
5.18 + notifications. */
5.19 +
5.20 + get_task_notifier()->unsubscribe(file);
5.21
5.22 if (file->memory != NULL)
5.23 ipc_detach_dataspace(file->memory);
5.24 @@ -382,21 +383,18 @@
5.25
5.26 /* Subscribe to notification events on a file. */
5.27
5.28 -long file_notify_subscribe(file_t *file, notify_flags_t flags)
5.29 +long file_notify_subscribe(file_t *file, notify_flags_t flags, notifier_t notifier_type)
5.30 {
5.31 - FileNotifier *notifier = get_notifier();
5.32 + FileNotifier *notifier = get_notifier(notifier_type);
5.33
5.34 return notifier->subscribe(file, flags);
5.35 }
5.36
5.37 /* Unsubscribe from notification events on a file. */
5.38
5.39 -long file_notify_unsubscribe(file_t *file)
5.40 +long file_notify_unsubscribe(file_t *file, notifier_t notifier_type)
5.41 {
5.42 - if (l4_is_invalid_cap(file->notifier))
5.43 - return -L4_EINVAL;
5.44 -
5.45 - FileNotifier *notifier = get_notifier();
5.46 + FileNotifier *notifier = get_notifier(notifier_type);
5.47
5.48 return notifier->unsubscribe(file);
5.49 }
5.50 @@ -405,7 +403,7 @@
5.51
5.52 long file_notify_wait_file(file_t *file)
5.53 {
5.54 - FileNotifier *notifier = get_notifier();
5.55 + SpecificFileNotifier *notifier = get_task_notifier();
5.56
5.57 return notifier->wait_file(file);
5.58 }
5.59 @@ -414,7 +412,7 @@
5.60
5.61 long file_notify_wait_files(file_t **file)
5.62 {
5.63 - FileNotifier *notifier = get_notifier();
5.64 + GeneralFileNotifier *notifier = get_thread_notifier();
5.65
5.66 return notifier->wait(file);
5.67 }
6.1 --- a/libfsclient/lib/src/notifier.cc Sat Aug 14 18:41:57 2021 +0200
6.2 +++ b/libfsclient/lib/src/notifier.cc Tue Aug 17 23:44:36 2021 +0200
6.3 @@ -33,93 +33,88 @@
6.4
6.5
6.6
6.7 -/* Thread-local storage workaround. */
6.8 +/* Null notification state. */
6.9 +
6.10 +static FileNotificationState _null_state;
6.11 +
6.12 +
6.13 +
6.14 +/* Lock protecting per-task notifier access. */
6.15 +
6.16 +static std::mutex _lock;
6.17 +
6.18 +/* Per-task storage for specific waiting operations. */
6.19 +
6.20 +static SpecificFileNotifier *_notifier = NULL;
6.21 +
6.22 +/* Per-thread storage for "open" waiting operations.
6.23 + (This workaround for thread-local storage maps thread capabilities to
6.24 + notifiers). */
6.25 +
6.26 +static std::map<l4_cap_idx_t, GeneralFileNotifier *> _notifiers;
6.27 +
6.28 +
6.29
6.30 -static std::mutex lock;
6.31 -static std::map<l4_cap_idx_t, FileNotifier *> notifiers;
6.32 +/* Return the per-task notifier for file-specific waiting operations. */
6.33 +
6.34 +SpecificFileNotifier *get_task_notifier()
6.35 +{
6.36 + std::lock_guard<std::mutex> guard(_lock);
6.37 +
6.38 + /* Start any new notifier. */
6.39
6.40 -FileNotifier *get_notifier()
6.41 + if (_notifier == NULL)
6.42 + {
6.43 + _notifier = new SpecificFileNotifier;
6.44 + _notifier->start();
6.45 + }
6.46 +
6.47 + return _notifier;
6.48 +}
6.49 +
6.50 +/* Return the per-thread notifier for general file waiting operations. */
6.51 +
6.52 +GeneralFileNotifier *get_thread_notifier()
6.53 {
6.54 - std::lock_guard<std::mutex> guard(lock);
6.55 + std::lock_guard<std::mutex> guard(_lock);
6.56
6.57 l4_cap_idx_t thread = pthread_l4_cap(pthread_self());
6.58 - FileNotifier *notifier = notifiers[thread];
6.59 + GeneralFileNotifier *notifier = _notifiers[thread];
6.60
6.61 /* Start any new notifier. */
6.62
6.63 if (notifier == NULL)
6.64 {
6.65 - notifier = new FileNotifier;
6.66 - notifiers[thread] = notifier;
6.67 + notifier = new GeneralFileNotifier;
6.68 + _notifiers[thread] = notifier;
6.69 notifier->start();
6.70 }
6.71
6.72 return notifier;
6.73 }
6.74
6.75 -
6.76 -
6.77 -/* Subscribe to notification events on a file. */
6.78 +/* Helper function to obtain the appropriate notifier. */
6.79
6.80 -long FileNotifier::subscribe(file_t *file, notify_flags_t flags)
6.81 +FileNotifier *get_notifier(notifier_t notifier_type)
6.82 {
6.83 - /* Create a notification endpoint, if necessary. */
6.84 -
6.85 - if (l4_is_invalid_cap(file->notifier))
6.86 + switch (notifier_type)
6.87 {
6.88 - long err = ipc_server_new_for_thread(&file->notifier, file, _thread);
6.89 -
6.90 - if (err)
6.91 - return err;
6.92 + case NOTIFIER_TASK: return get_task_notifier();
6.93 + case NOTIFIER_THREAD: return get_thread_notifier();
6.94 + default: return NULL;
6.95 }
6.96 -
6.97 - client_Notification notify(file->ref);
6.98 -
6.99 - return notify.subscribe(file->notifier, flags);
6.100 }
6.101
6.102 -/* Unsubscribe from notification events on a file. */
6.103
6.104 -long FileNotifier::unsubscribe(file_t *file)
6.105 -{
6.106 - if (l4_is_invalid_cap(file->notifier))
6.107 - return -L4_EINVAL;
6.108 -
6.109 - client_Notification notify(file->ref);
6.110
6.111 - long err = notify.unsubscribe(file->notifier);
6.112 -
6.113 - if (err)
6.114 - return err;
6.115 -
6.116 - ipc_cap_free_um(file->notifier);
6.117 - return L4_EOK;
6.118 -}
6.119 +/* Invoke the mainloop in a thread. */
6.120
6.121 -/* Handle a notification event for a file. Ideally, this would be invoked by the
6.122 - generic server dispatch mechanism, with the gate label being interpreted and
6.123 - provided as the first parameter. */
6.124 -
6.125 -void FileNotifier::_notify(file_t *file, notify_flags_t flags)
6.126 +static void *notifier_mainloop(void *data)
6.127 {
6.128 - std::unique_lock<std::mutex> guard(_lock);
6.129 -
6.130 - /* Record the flags for the file object. Where no flags are already recorded,
6.131 - the new flags will be recorded and the file object queued. Otherwise, the
6.132 - new flags will be combined with the recorded flags. */
6.133 + FileNotifier *notifier = reinterpret_cast<FileNotifier *>(data);
6.134
6.135 - notify_flags_t recorded = _affected_flags[file];
6.136 -
6.137 - _affected_flags[file] = recorded | flags;
6.138 -
6.139 - /* Add a file queue entry for any files without recorded notifications. */
6.140 -
6.141 - if (!recorded)
6.142 - _affected.push_back(file);
6.143 -
6.144 - /* Notify any waiting caller. */
6.145 -
6.146 - _notified.notify_one();
6.147 + notifier->mainloop();
6.148 + return 0;
6.149 }
6.150
6.151 /* Listen for notifications. */
6.152 @@ -165,16 +160,6 @@
6.153 ipc_message_free(&msg);
6.154 }
6.155
6.156 -/* Invoke the mainloop in a thread. */
6.157 -
6.158 -static void *notifier_mainloop(void *data)
6.159 -{
6.160 - FileNotifier *notifier = reinterpret_cast<FileNotifier *>(data);
6.161 -
6.162 - notifier->mainloop();
6.163 - return 0;
6.164 -}
6.165 -
6.166 /* Start listening for notifications. */
6.167
6.168 long FileNotifier::start()
6.169 @@ -199,36 +184,238 @@
6.170 return L4_EOK;
6.171 }
6.172
6.173 +
6.174 +
6.175 +/* Return a notification state object for the given file or a null object if no
6.176 + record existed for the file. */
6.177 +
6.178 +FileNotificationState &FileNotifier::file_state(file_t *file, bool create)
6.179 +{
6.180 + FileNotificationStates::iterator it = _state.find(file);
6.181 +
6.182 + if (it == _state.end())
6.183 + {
6.184 + if (create)
6.185 + return _state[file];
6.186 + else
6.187 + return _null_state;
6.188 + }
6.189 +
6.190 + return it->second;
6.191 +}
6.192 +
6.193 +/* Subscribe to notification events on a file. */
6.194 +
6.195 +long FileNotifier::subscribe(file_t *file, notify_flags_t flags)
6.196 +{
6.197 + /* Acquire the lock for state lookup. */
6.198 +
6.199 + std::unique_lock<std::mutex> state_guard(_state_lock);
6.200 +
6.201 + FileNotificationState &state = file_state(file, true);
6.202 +
6.203 + /* Create a notification endpoint, if necessary. */
6.204 +
6.205 + if (state.is_null())
6.206 + {
6.207 + long err = ipc_server_new_for_thread(&state.endpoint, file, _thread);
6.208 +
6.209 + if (err)
6.210 + return err;
6.211 + }
6.212 +
6.213 + client_Notification notify(file->ref);
6.214 +
6.215 + return notify.subscribe(state.endpoint, flags);
6.216 +}
6.217 +
6.218 +/* Unsubscribe from notification events on a file. */
6.219 +
6.220 +long FileNotifier::unsubscribe(file_t *file)
6.221 +{
6.222 + /* Acquire the lock for state lookup. */
6.223 +
6.224 + std::unique_lock<std::mutex> state_guard(_state_lock);
6.225 +
6.226 + FileNotificationState &state = file_state(file, false);
6.227 +
6.228 + if (state.is_null())
6.229 + return -L4_EINVAL;
6.230 +
6.231 + client_Notification notify(file->ref);
6.232 +
6.233 + long err = notify.unsubscribe(state.endpoint);
6.234 +
6.235 + if (err)
6.236 + return err;
6.237 +
6.238 + _unsubscribe(state, file);
6.239 +
6.240 + /* Remove the lock for updating file state. */
6.241 +
6.242 + _file_locks.erase(file);
6.243 +
6.244 + return L4_EOK;
6.245 +}
6.246 +
6.247 +/* Remove file notification state from the notifier. */
6.248 +
6.249 +void FileNotifier::_unsubscribe(FileNotificationState &state, file_t *file)
6.250 +{
6.251 + /* Acquire the lock for updating file state. */
6.252 +
6.253 + std::mutex &file_lock = _file_locks[file];
6.254 + std::unique_lock<std::mutex> file_guard(file_lock);
6.255 +
6.256 + /* Remove file-specific state. */
6.257 +
6.258 + ipc_cap_free_um(state.endpoint);
6.259 + _state.erase(file);
6.260 +}
6.261 +
6.262 +
6.263 +
6.264 +/* Handle a notification event for a file. Ideally, this would be invoked by the
6.265 + generic server dispatch mechanism, with the gate label being interpreted and
6.266 + provided as the first parameter. */
6.267 +
6.268 +void GeneralFileNotifier::_notify(file_t *file, notify_flags_t flags)
6.269 +{
6.270 + /* Enter critical section for the notifier (affecting all files). */
6.271 +
6.272 + std::unique_lock<std::mutex> general_guard(_general_lock);
6.273 +
6.274 + /* Acquire the lock for state lookup. */
6.275 +
6.276 + std::unique_lock<std::mutex> state_guard(_state_lock);
6.277 +
6.278 + FileNotificationState &state = file_state(file, false);
6.279 +
6.280 + if (state.is_null())
6.281 + return;
6.282 +
6.283 + /* Acquire the lock for the file state itself. */
6.284 +
6.285 + std::unique_lock<std::mutex> file_guard(state.lock);
6.286 +
6.287 + /* Record flags and return previous flags. */
6.288 +
6.289 + notify_flags_t recorded = state.pending;
6.290 +
6.291 + state.pending |= flags;
6.292 +
6.293 + /* Add a file queue entry for any files without previous notifications. */
6.294 +
6.295 + if (!recorded)
6.296 + _affected.push_back(file);
6.297 +
6.298 + /* Notify any waiting caller. */
6.299 +
6.300 + _general_condition.notify_one();
6.301 +}
6.302 +
6.303 +void SpecificFileNotifier::_notify(file_t *file, notify_flags_t flags)
6.304 +{
6.305 + /* Acquire the lock for state lookup. */
6.306 +
6.307 + std::unique_lock<std::mutex> state_guard(_state_lock);
6.308 +
6.309 + FileNotificationState &state = file_state(file, false);
6.310 +
6.311 + if (state.is_null())
6.312 + return;
6.313 +
6.314 + /* Acquire the lock for the file state itself. */
6.315 +
6.316 + std::unique_lock<std::mutex> file_guard(state.lock);
6.317 +
6.318 + state.pending |= flags;
6.319 +
6.320 + /* Notify any waiting caller. */
6.321 +
6.322 + state.condition.notify_one();
6.323 +}
6.324 +
6.325 +
6.326 +
6.327 +/* Transfer pending notifications to the given file. This must be called with a
6.328 + lock acquired on the file notification state. */
6.329 +
6.330 +bool FileNotifier::_transfer(FileNotificationState &state, file_t *file)
6.331 +{
6.332 + notify_flags_t recorded = state.pending;
6.333 +
6.334 + if (recorded)
6.335 + {
6.336 + file->notifications = recorded;
6.337 + state.pending = 0;
6.338 + return true;
6.339 + }
6.340 +
6.341 + return false;
6.342 +}
6.343 +
6.344 +
6.345 +
6.346 +/* Obtain file state and transfer notifications. */
6.347 +
6.348 +bool GeneralFileNotifier::_retrieve_for_file(file_t *file)
6.349 +{
6.350 + /* Acquire the lock for state lookup. */
6.351 +
6.352 + std::unique_lock<std::mutex> state_guard(_state_lock);
6.353 +
6.354 + FileNotificationState &state = file_state(file, false);
6.355 +
6.356 + if (state.is_null())
6.357 + return false;
6.358 +
6.359 + /* Acquire the lock for the file state itself, then release the state lock. */
6.360 +
6.361 + std::unique_lock<std::mutex> file_guard(state.lock);
6.362 +
6.363 + state_guard.unlock();
6.364 +
6.365 + /* Call generic method to transfer notifications, if possible. */
6.366 +
6.367 + return _transfer(state, file);
6.368 +}
6.369 +
6.370 +/* Obtain queued files until one is found that still has events recorded for it.
6.371 + This must be called with the notifier's general lock acquired. */
6.372 +
6.373 +bool GeneralFileNotifier::_retrieve(file_t **file)
6.374 +{
6.375 + while (!_affected.empty())
6.376 + {
6.377 + *file = _affected.front();
6.378 + _affected.pop_front();
6.379 +
6.380 + if (_retrieve_for_file(*file))
6.381 + return true;
6.382 + }
6.383 +
6.384 + return false;
6.385 +}
6.386 +
6.387 +
6.388 +
6.389 /* Wait for notification events on files. */
6.390
6.391 -long FileNotifier::wait(file_t **file)
6.392 +long GeneralFileNotifier::wait(file_t **file)
6.393 {
6.394 - std::unique_lock<std::mutex> guard(_lock);
6.395 + std::unique_lock<std::mutex> general_guard(_general_lock);
6.396
6.397 while (1)
6.398 {
6.399 - /* Obtain queued files until one is found that still has events recorded
6.400 - for it. (Waiting for events specific to one file will remove recorded
6.401 - events but not any file queue entries.) */
6.402 -
6.403 - while (!_affected.empty())
6.404 - {
6.405 - *file = _affected.front();
6.406 - _affected.pop_front();
6.407 -
6.408 - notify_flags_t recorded = _affected_flags[*file];
6.409 + /* With pending notifications, update the first file and exit. */
6.410
6.411 - if (recorded)
6.412 - {
6.413 - (*file)->notifications = recorded;
6.414 - _affected_flags.erase(*file);
6.415 - return L4_EOK;
6.416 - }
6.417 - }
6.418 + if (_retrieve(file))
6.419 + break;
6.420
6.421 - /* No queued events. */
6.422 + /* Otherwise, wait for notifications. */
6.423
6.424 - _notified.wait(guard);
6.425 + _general_condition.wait(general_guard);
6.426 }
6.427
6.428 return L4_EOK;
6.429 @@ -236,24 +423,33 @@
6.430
6.431 /* Wait for notifications from a single file. */
6.432
6.433 -long FileNotifier::wait_file(file_t *file)
6.434 +long SpecificFileNotifier::wait_file(file_t *file)
6.435 {
6.436 - std::unique_lock<std::mutex> guard(_lock);
6.437 + /* Acquire the lock for reading file state. */
6.438 +
6.439 + std::unique_lock<std::mutex> state_guard(_state_lock);
6.440 +
6.441 + FileNotificationState &state = file_state(file, false);
6.442 +
6.443 + if (state.is_null())
6.444 + return -L4_EINVAL;
6.445 +
6.446 + /* Acquire the lock for the file state itself, then release the state lock. */
6.447 +
6.448 + std::unique_lock<std::mutex> file_guard(state.lock);
6.449 +
6.450 + state_guard.unlock();
6.451
6.452 while (1)
6.453 {
6.454 - notify_flags_t recorded = _affected_flags[file];
6.455 + /* With pending notifications, update the file and exit. */
6.456
6.457 - if (recorded)
6.458 - {
6.459 - file->notifications = recorded;
6.460 - _affected_flags.erase(file);
6.461 - return L4_EOK;
6.462 - }
6.463 + if (_transfer(state, file))
6.464 + break;
6.465
6.466 - /* No recorded events for the file. */
6.467 + /* Otherwise, wait for notifications. */
6.468
6.469 - _notified.wait(guard);
6.470 + state.condition.wait(file_guard);
6.471 }
6.472
6.473 return L4_EOK;
7.1 --- a/tests/dstest_file_client.cc Sat Aug 14 18:41:57 2021 +0200
7.2 +++ b/tests/dstest_file_client.cc Tue Aug 17 23:44:36 2021 +0200
7.3 @@ -61,7 +61,7 @@
7.4
7.5 /* Register for notifications. */
7.6
7.7 - if ((err = client_subscribe(file, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED)))
7.8 + if ((err = client_subscribe(file, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED, NOTIFIER_TASK)))
7.9 {
7.10 printf("Could not subscribe to notifications: %s\n", l4sys_errtostr(err));
7.11 return;
8.1 --- a/tests/dstest_pipe_client.cc Sat Aug 14 18:41:57 2021 +0200
8.2 +++ b/tests/dstest_pipe_client.cc Tue Aug 17 23:44:36 2021 +0200
8.3 @@ -79,8 +79,8 @@
8.4
8.5 /* Register the readers for notification. */
8.6
8.7 - if ((err = client_subscribe(reader1, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED)) ||
8.8 - (err = client_subscribe(reader2, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED)))
8.9 + if ((err = client_subscribe(reader1, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED, NOTIFIER_THREAD)) ||
8.10 + (err = client_subscribe(reader2, NOTIFY_CONTENT_AVAILABLE | NOTIFY_PEER_CLOSED, NOTIFIER_THREAD)))
8.11 {
8.12 printf("Could not subscribe to notifications: %s\n", l4sys_errtostr(err));
8.13 return;