1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/libfsclient/lib/src/notifier.cc Tue Jul 06 00:45:25 2021 +0200
1.3 @@ -0,0 +1,205 @@
1.4 +/*
1.5 + * File event notification support.
1.6 + *
1.7 + * Copyright (C) 2021 Paul Boddie <paul@boddie.org.uk>
1.8 + *
1.9 + * This program is free software; you can redistribute it and/or
1.10 + * modify it under the terms of the GNU General Public License as
1.11 + * published by the Free Software Foundation; either version 2 of
1.12 + * the License, or (at your option) any later version.
1.13 + *
1.14 + * This program is distributed in the hope that it will be useful,
1.15 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1.16 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1.17 + * GNU General Public License for more details.
1.18 + *
1.19 + * You should have received a copy of the GNU General Public License
1.20 + * along with this program; if not, write to the Free Software
1.21 + * Foundation, Inc., 51 Franklin Street, Fifth Floor,
1.22 + * Boston, MA 02110-1301, USA
1.23 + */
1.24 +
1.25 +#include <map>
1.26 +#include <mutex>
1.27 +
1.28 +#include <ipc/cap_alloc.h>
1.29 +#include <ipc/server.h>
1.30 +
1.31 +#include <pthread.h>
1.32 +#include <pthread-l4.h>
1.33 +
1.34 +#include "notification_client.h"
1.35 +#include "notifier.h"
1.36 +
1.37 +
1.38 +
1.39 +/* Thread-local storage workaround. */
1.40 +
1.41 +static std::mutex lock;
1.42 +static std::map<l4_cap_idx_t, FileNotifier *> notifiers;
1.43 +
1.44 +FileNotifier *get_notifier()
1.45 +{
1.46 + std::lock_guard<std::mutex> guard(lock);
1.47 +
1.48 + l4_cap_idx_t thread = pthread_l4_cap(pthread_self());
1.49 + FileNotifier *notifier = notifiers[thread];
1.50 +
1.51 + /* Start any new notifier. */
1.52 +
1.53 + if (notifier == NULL)
1.54 + {
1.55 + notifier = new FileNotifier;
1.56 + notifiers[thread] = notifier;
1.57 + notifier->start();
1.58 + }
1.59 +
1.60 + return notifier;
1.61 +}
1.62 +
1.63 +
1.64 +
1.65 +/* Subscribe to notification events on a file. */
1.66 +
1.67 +long FileNotifier::subscribe(file_t *file, notify_flags_t flags)
1.68 +{
1.69 + /* Create a notification endpoint, if necessary. */
1.70 +
1.71 + if (l4_is_invalid_cap(file->notifier))
1.72 + {
1.73 + long err = ipc_server_new_for_thread(&file->notifier, file, _thread);
1.74 +
1.75 + if (err)
1.76 + return err;
1.77 + }
1.78 +
1.79 + client_Notification notify(file->ref);
1.80 +
1.81 + return notify.subscribe(file->notifier, flags);
1.82 +}
1.83 +
1.84 +/* Unsubscribe from notification events on a file. */
1.85 +
1.86 +long FileNotifier::unsubscribe(file_t *file)
1.87 +{
1.88 + if (l4_is_invalid_cap(file->notifier))
1.89 + return -L4_EINVAL;
1.90 +
1.91 + ipc_cap_free_um(file->notifier);
1.92 +
1.93 + client_Notification notify(file->ref);
1.94 +
1.95 + return notify.unsubscribe();
1.96 +}
1.97 +
1.98 +/* Handle a notification event for a file. Ideally, this would be invoked by the
1.99 + generic server dispatch mechanism, with the gate label being interpreted and
1.100 + provided as the first parameter. */
1.101 +
1.102 +void FileNotifier::_notify(file_t *file, notify_flags_t flags)
1.103 +{
1.104 + std::unique_lock<std::mutex> guard(_lock);
1.105 +
1.106 + /* Record the flags in the file object. */
1.107 +
1.108 + file->notifications = flags;
1.109 + _affected.push_back(file);
1.110 +
1.111 + /* Notify any waiting caller. */
1.112 +
1.113 + _notified.notify_one();
1.114 +}
1.115 +
1.116 +/* Listen for notifications. */
1.117 +
1.118 +void FileNotifier::mainloop()
1.119 +{
1.120 + ipc_message_t msg;
1.121 + l4_umword_t label;
1.122 +
1.123 + while (1)
1.124 + {
1.125 + ipc_message_wait(&msg, &label);
1.126 +
1.127 + /* Clear lower label bits. */
1.128 +
1.129 + label = label & ~3UL;
1.130 +
1.131 + /* Ignore erroneous messages. */
1.132 +
1.133 + if (l4_ipc_error(msg.tag, l4_utcb()))
1.134 + continue;
1.135 +
1.136 + /* Reply to notifications. */
1.137 +
1.138 + ipc_message_reply(&msg);
1.139 +
1.140 + /* Interpret gate labels as file objects. */
1.141 +
1.142 + file_t *file = (file_t *) label;
1.143 + notify_flags_t flags = ipc_message_get_word(&msg, 0);
1.144 +
1.145 + /* Register the notification. */
1.146 +
1.147 + _notify(file, flags);
1.148 + }
1.149 +
1.150 + ipc_message_free(&msg);
1.151 +}
1.152 +
1.153 +/* Invoke the mainloop in a thread. */
1.154 +
1.155 +static void *notifier_mainloop(void *data)
1.156 +{
1.157 + FileNotifier *notifier = reinterpret_cast<FileNotifier *>(data);
1.158 +
1.159 + notifier->mainloop();
1.160 + return 0;
1.161 +}
1.162 +
1.163 +/* Start listening for notifications. */
1.164 +
1.165 +long FileNotifier::start()
1.166 +{
1.167 + if (_started)
1.168 + return L4_EOK;
1.169 +
1.170 + pthread_t thread;
1.171 + pthread_attr_t attr;
1.172 + long err;
1.173 +
1.174 + pthread_attr_init(&attr);
1.175 + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
1.176 +
1.177 + err = pthread_create(&thread, &attr, notifier_mainloop, this);
1.178 + if (err)
1.179 + return err;
1.180 +
1.181 + _thread = pthread_l4_cap(thread);
1.182 + _started = true;
1.183 +
1.184 + return L4_EOK;
1.185 +}
1.186 +
1.187 +/* Wait for notification events on files. */
1.188 +
1.189 +long FileNotifier::wait(file_t **file)
1.190 +{
1.191 + std::unique_lock<std::mutex> guard(_lock);
1.192 +
1.193 + while (1)
1.194 + {
1.195 + if (!_affected.empty())
1.196 + {
1.197 + *file = _affected.front();
1.198 + _affected.pop_front();
1.199 + return L4_EOK;
1.200 + }
1.201 + else
1.202 + _notified.wait(guard);
1.203 + }
1.204 +
1.205 + return L4_EOK;
1.206 +}
1.207 +
1.208 +// vim: tabstop=2 expandtab shiftwidth=2