1 /* 2 * Notification support. 3 * 4 * Copyright (C) 2021, 2022, 2023 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <ipc/cap_alloc.h> 23 24 #include "notification.h" 25 #include "notifier_client.h" 26 27 #include "resource_server.h" 28 29 30 31 /* Initialise endpoints and flags for notifications. */ 32 33 NotificationSupport::NotificationSupport(unsigned int endpoints) 34 : _min_endpoints(endpoints) 35 { 36 _endpoints.resize(_min_endpoints); 37 } 38 39 NotificationSupport::~NotificationSupport() 40 { 41 } 42 43 /* Subscribe to notifications using a notification object, returning the 44 endpoint number. */ 45 46 long NotificationSupport::subscribe(l4_cap_idx_t notifier, notify_flags_t flags, 47 void *subscriber, unsigned int *endpoint_number) 48 { 49 std::lock_guard<std::mutex> guard(_lock); 50 51 unsigned int endpoint = _endpoints.size(); 52 _endpoints.resize(endpoint + 1); 53 54 /* Propagate deferred flags for new endpoints. */ 55 56 if (_deferred) 57 _endpoints[endpoint].deferred = _deferred; 58 59 *endpoint_number = endpoint; 60 return _subscribe(endpoint, notifier, flags, subscriber); 61 } 62 63 /* Subscribe to a specific endpoint's notifications using a notification 64 object. */ 65 66 long NotificationSupport::subscribe(unsigned int endpoint, l4_cap_idx_t notifier, 67 notify_flags_t flags, void *subscriber) 68 { 69 std::lock_guard<std::mutex> guard(_lock); 70 71 if (endpoint >= _endpoints.size()) 72 _endpoints.resize(endpoint + 1); 73 74 /* Propagate deferred flags for new endpoints. */ 75 76 if (_deferred) 77 _endpoints[endpoint].deferred = _deferred; 78 79 return _subscribe(endpoint, notifier, flags, subscriber); 80 } 81 82 /* Unsubscribe from an endpoint's notifications. */ 83 84 void NotificationSupport::unsubscribe(unsigned int endpoint, void *subscriber) 85 { 86 std::lock_guard<std::mutex> guard(_lock); 87 88 _unsubscribe(endpoint, subscriber); 89 } 90 91 /* Common subscription functionality. */ 92 93 long NotificationSupport::_subscribe(unsigned int endpoint, l4_cap_idx_t notifier, 94 notify_flags_t flags, void *subscriber) 95 { 96 /* Record details of the notifier itself. */ 97 98 NotificationEndpoint &ep = _endpoints[endpoint]; 99 100 ep.notifiers.insert(notifier); 101 ep.flags = flags; 102 103 /* Send deferred conditions held from before subscription occurred. */ 104 105 if (ep.deferred) 106 { 107 _notify(endpoint, ep.deferred); 108 ep.deferred = 0; 109 } 110 111 /* Record the subscriber's notifier. */ 112 113 _subscribers[subscriber] = notifier; 114 return L4_EOK; 115 } 116 117 void NotificationSupport::_unsubscribe(unsigned int endpoint, void *subscriber) 118 { 119 if (endpoint >= _endpoints.size()) 120 return; 121 122 /* Find and remove the subscriber's notifier. */ 123 124 SubscriberNotifierMap::iterator itp = _subscribers.find(subscriber); 125 l4_cap_idx_t notifier; 126 127 if (itp == _subscribers.end()) 128 return; 129 130 notifier = itp->second; 131 _subscribers.erase(itp); 132 133 /* Release the notifier. */ 134 135 NotificationEndpoint &ep = _endpoints[endpoint]; 136 NotifierSet::iterator it = ep.notifiers.find(notifier); 137 138 if (it != ep.notifiers.end()) 139 { 140 ep.notifiers.erase(it); 141 ipc_cap_free_um(notifier); 142 143 if (ep.notifiers.empty()) 144 { 145 ep.flags = 0; 146 ep.deferred = 0; 147 } 148 } 149 } 150 151 /* Notify a particular endpoint. */ 152 153 void NotificationSupport::notify(unsigned int endpoint, notify_flags_t flags) 154 { 155 std::lock_guard<std::mutex> guard(_lock); 156 157 _notify(endpoint, flags); 158 } 159 160 void NotificationSupport::_notify(unsigned int endpoint, notify_flags_t flags) 161 { 162 if (endpoint >= _endpoints.size()) 163 return; 164 165 NotificationEndpoint &ep = _endpoints[endpoint]; 166 167 /* Notify the endpoint or hold any notification for potential future 168 subscription. */ 169 170 if (!ep.notifiers.empty()) 171 { 172 if (flags & ep.flags) 173 { 174 NotifierSet::iterator it; 175 176 for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++) 177 { 178 client_Notifier notifier(*it); 179 180 notifier.notify(flags & ep.flags, (notify_values_t) NOTIFY_VALUES_NULL); 181 } 182 } 183 } 184 else 185 ep.deferred = flags; 186 } 187 188 /* Notify all endpoints. */ 189 190 void NotificationSupport::notify_all(notify_flags_t flags) 191 { 192 std::lock_guard<std::mutex> guard(_lock); 193 194 for (unsigned int i = 0; i < _endpoints.size(); i++) 195 _notify(i, flags); 196 197 _deferred |= flags; 198 } 199 200 /* Notify the other endpoints. */ 201 202 void NotificationSupport::notify_others(unsigned int endpoint, notify_flags_t flags) 203 { 204 std::lock_guard<std::mutex> guard(_lock); 205 206 for (unsigned int i = 0; i < _endpoints.size(); i++) 207 if (i != endpoint) 208 _notify(i, flags); 209 210 _deferred |= flags; 211 } 212 213 /* Release notifiers for each endpoint. */ 214 215 void NotificationSupport::release_notifiers() 216 { 217 std::lock_guard<std::mutex> guard(_lock); 218 219 for (unsigned int endpoint = 0; endpoint < _endpoints.size(); endpoint++) 220 { 221 NotificationEndpoint &ep = _endpoints[endpoint]; 222 NotifierSet::iterator it; 223 224 for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++) 225 ipc_cap_free_um(*it); 226 227 ep.notifiers.clear(); 228 ep.flags = 0; 229 ep.deferred = 0; 230 } 231 } 232 233 // vim: tabstop=4 expandtab shiftwidth=4