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 #include "notifier_peer_endpoint.h" 27 28 #include "resource_server.h" 29 30 31 32 /* Initialise endpoints and flags for notifications. */ 33 34 NotificationSupport::NotificationSupport(unsigned int endpoints) 35 : _min_endpoints(endpoints) 36 { 37 _endpoints.resize(_min_endpoints); 38 } 39 40 NotificationSupport::~NotificationSupport() 41 { 42 } 43 44 /* Subscribe to notifications using a notification object, returning the 45 endpoint number. */ 46 47 long NotificationSupport::subscribe(l4_cap_idx_t notifier, notify_flags_t flags, 48 l4_cap_idx_t *peer, unsigned int *endpoint_number) 49 { 50 std::lock_guard<std::mutex> guard(_lock); 51 52 unsigned int endpoint = _endpoints.size(); 53 _endpoints.resize(endpoint + 1); 54 55 /* Propagate deferred flags for new endpoints. */ 56 57 if (_deferred) 58 _endpoints[endpoint].deferred = _deferred; 59 60 *endpoint_number = endpoint; 61 return _subscribe(endpoint, notifier, flags, peer); 62 } 63 64 /* Subscribe to a specific endpoint's notifications using a notification 65 object. */ 66 67 long NotificationSupport::subscribe(unsigned int endpoint, l4_cap_idx_t notifier, 68 notify_flags_t flags, l4_cap_idx_t *peer) 69 { 70 std::lock_guard<std::mutex> guard(_lock); 71 72 if (endpoint >= _endpoints.size()) 73 { 74 _endpoints.resize(endpoint + 1); 75 76 /* Propagate deferred flags for new endpoints. */ 77 78 if (_deferred) 79 _endpoints[endpoint].deferred = _deferred; 80 } 81 82 return _subscribe(endpoint, notifier, flags, peer); 83 } 84 85 /* Unsubscribe from an endpoint's notifications. */ 86 87 void NotificationSupport::unsubscribe(unsigned int endpoint, l4_cap_idx_t notifier) 88 { 89 std::lock_guard<std::mutex> guard(_lock); 90 91 _unsubscribe(endpoint, notifier); 92 } 93 94 /* Common subscription functionality. */ 95 96 long NotificationSupport::_subscribe(unsigned int endpoint, l4_cap_idx_t notifier, 97 notify_flags_t flags, l4_cap_idx_t *peer) 98 { 99 /* Record details of the notifier itself. */ 100 101 NotificationEndpoint &ep = _endpoints[endpoint]; 102 103 ep.notifiers.insert(notifier); 104 ep.flags = flags; 105 106 /* Send deferred conditions held from before subscription occurred. */ 107 108 if (ep.deferred) 109 { 110 _notify(endpoint, ep.deferred); 111 ep.deferred = 0; 112 } 113 114 /* Create a notifier peer for unsubscribing. */ 115 116 NotifierPeerEndpoint *peer_ep = new NotifierPeerEndpoint(this, endpoint, notifier); 117 118 _peers[notifier] = peer_ep; 119 120 long err = ResourceServer(peer_ep).start_thread(peer); 121 122 if (err) 123 _unsubscribe(endpoint, notifier); 124 125 return err; 126 } 127 128 void NotificationSupport::_unsubscribe(unsigned int endpoint, l4_cap_idx_t notifier) 129 { 130 if (endpoint >= _endpoints.size()) 131 return; 132 133 NotificationEndpoint &ep = _endpoints[endpoint]; 134 NotifierSet::iterator it = ep.notifiers.find(notifier); 135 136 if (it != ep.notifiers.end()) 137 { 138 ep.notifiers.erase(it); 139 ipc_cap_free_um(notifier); 140 141 if (ep.notifiers.empty()) 142 { 143 ep.flags = 0; 144 ep.deferred = 0; 145 } 146 } 147 148 /* Remove the notifier peer. */ 149 150 NotifierPeerMap::iterator itp = _peers.find(notifier); 151 152 if (itp != _peers.end()) 153 _peers.erase(itp); 154 } 155 156 /* Notify a particular endpoint. */ 157 158 void NotificationSupport::notify(unsigned int endpoint, notify_flags_t flags) 159 { 160 std::lock_guard<std::mutex> guard(_lock); 161 162 _notify(endpoint, flags); 163 } 164 165 void NotificationSupport::_notify(unsigned int endpoint, notify_flags_t flags) 166 { 167 if (endpoint >= _endpoints.size()) 168 return; 169 170 NotificationEndpoint &ep = _endpoints[endpoint]; 171 172 /* Notify the endpoint or hold any notification for potential future 173 subscription. */ 174 175 if (!ep.notifiers.empty()) 176 { 177 if (flags & ep.flags) 178 { 179 NotifierSet::iterator it; 180 181 for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++) 182 { 183 client_Notifier notifier(*it); 184 185 notifier.notify(flags & ep.flags); 186 } 187 } 188 } 189 else 190 ep.deferred = flags; 191 } 192 193 /* Notify all endpoints. */ 194 195 void NotificationSupport::notify_all(notify_flags_t flags) 196 { 197 std::lock_guard<std::mutex> guard(_lock); 198 199 for (unsigned int i = 0; i < _endpoints.size(); i++) 200 _notify(i, flags); 201 202 _deferred |= flags; 203 } 204 205 /* Notify the other endpoints. */ 206 207 void NotificationSupport::notify_others(unsigned int endpoint, notify_flags_t flags) 208 { 209 std::lock_guard<std::mutex> guard(_lock); 210 211 for (unsigned int i = 0; i < _endpoints.size(); i++) 212 if (i != endpoint) 213 _notify(i, flags); 214 215 _deferred |= flags; 216 } 217 218 /* Release notifiers for each endpoint. */ 219 220 void NotificationSupport::release_notifiers() 221 { 222 std::lock_guard<std::mutex> guard(_lock); 223 224 for (unsigned int endpoint = 0; endpoint < _endpoints.size(); endpoint++) 225 { 226 NotificationEndpoint &ep = _endpoints[endpoint]; 227 NotifierSet::iterator it; 228 229 for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++) 230 ipc_cap_free_um(*it); 231 232 ep.notifiers.clear(); 233 ep.flags = 0; 234 ep.deferred = 0; 235 } 236 } 237 238 // vim: tabstop=4 expandtab shiftwidth=4