1 /* 2 * Notification support. 3 * 4 * Copyright (C) 2021, 2022 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 { 73 _endpoints.resize(endpoint + 1); 74 75 /* Propagate deferred flags for new endpoints. */ 76 77 if (_deferred) 78 _endpoints[endpoint].deferred = _deferred; 79 } 80 81 return _subscribe(endpoint, notifier, flags, subscriber); 82 } 83 84 /* Unsubscribe from an endpoint's notifications. */ 85 86 void NotificationSupport::unsubscribe(unsigned int endpoint, void *subscriber) 87 { 88 std::lock_guard<std::mutex> guard(_lock); 89 90 _unsubscribe(endpoint, subscriber); 91 } 92 93 /* Common subscription functionality. */ 94 95 long NotificationSupport::_subscribe(unsigned int endpoint, l4_cap_idx_t notifier, 96 notify_flags_t flags, void *subscriber) 97 { 98 /* Record details of the notifier itself. */ 99 100 NotificationEndpoint &ep = _endpoints[endpoint]; 101 102 ep.notifiers.insert(notifier); 103 ep.flags = flags; 104 105 /* Send deferred conditions held from before subscription occurred. */ 106 107 if (ep.deferred) 108 { 109 _notify(endpoint, ep.deferred); 110 ep.deferred = 0; 111 } 112 113 /* Record the subscriber's notifier. */ 114 115 _subscribers[subscriber] = notifier; 116 return L4_EOK; 117 } 118 119 void NotificationSupport::_unsubscribe(unsigned int endpoint, void *subscriber) 120 { 121 if (endpoint >= _endpoints.size()) 122 return; 123 124 /* Find and remove the subscriber's notifier. */ 125 126 SubscriberNotifierMap::iterator itp = _subscribers.find(subscriber); 127 l4_cap_idx_t notifier; 128 129 if (itp == _subscribers.end()) 130 return; 131 132 notifier = itp->second; 133 _subscribers.erase(itp); 134 135 /* Release the notifier. */ 136 137 NotificationEndpoint &ep = _endpoints[endpoint]; 138 NotifierSet::iterator it = ep.notifiers.find(notifier); 139 140 if (it != ep.notifiers.end()) 141 { 142 ep.notifiers.erase(it); 143 ipc_cap_free_um(notifier); 144 145 if (ep.notifiers.empty()) 146 { 147 ep.flags = 0; 148 ep.deferred = 0; 149 } 150 } 151 } 152 153 /* Notify a particular endpoint. */ 154 155 void NotificationSupport::notify(unsigned int endpoint, notify_flags_t flags) 156 { 157 std::lock_guard<std::mutex> guard(_lock); 158 159 _notify(endpoint, flags); 160 } 161 162 void NotificationSupport::_notify(unsigned int endpoint, notify_flags_t flags) 163 { 164 if (endpoint >= _endpoints.size()) 165 return; 166 167 NotificationEndpoint &ep = _endpoints[endpoint]; 168 169 /* Notify the endpoint or hold any notification for potential future 170 subscription. */ 171 172 if (!ep.notifiers.empty()) 173 { 174 if (flags & ep.flags) 175 { 176 NotifierSet::iterator it; 177 178 for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++) 179 { 180 client_Notifier notifier(*it); 181 182 notifier.notify(flags & ep.flags); 183 } 184 } 185 } 186 else 187 ep.deferred = flags; 188 } 189 190 /* Notify all endpoints. */ 191 192 void NotificationSupport::notify_all(notify_flags_t flags) 193 { 194 std::lock_guard<std::mutex> guard(_lock); 195 196 for (unsigned int i = 0; i < _endpoints.size(); i++) 197 _notify(i, flags); 198 199 _deferred |= flags; 200 } 201 202 /* Notify the other endpoints. */ 203 204 void NotificationSupport::notify_others(unsigned int endpoint, notify_flags_t flags) 205 { 206 std::lock_guard<std::mutex> guard(_lock); 207 208 for (unsigned int i = 0; i < _endpoints.size(); i++) 209 if (i != endpoint) 210 _notify(i, flags); 211 212 _deferred |= flags; 213 } 214 215 /* Release notifiers for each endpoint. */ 216 217 void NotificationSupport::release_notifiers() 218 { 219 std::lock_guard<std::mutex> guard(_lock); 220 221 for (unsigned int endpoint = 0; endpoint < _endpoints.size(); endpoint++) 222 { 223 NotificationEndpoint &ep = _endpoints[endpoint]; 224 NotifierSet::iterator it; 225 226 for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++) 227 ipc_cap_free_um(*it); 228 229 ep.notifiers.clear(); 230 ep.flags = 0; 231 ep.deferred = 0; 232 } 233 } 234 235 // vim: tabstop=4 expandtab shiftwidth=4