1 /* 2 * Notification support. 3 * 4 * Copyright (C) 2021 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 28 29 /* Initialise endpoints and flags for notifications. */ 30 31 NotificationSupport::NotificationSupport(unsigned int endpoints) 32 : _min_endpoints(endpoints) 33 { 34 _endpoints.resize(_min_endpoints); 35 } 36 37 /* Subscribe to notifications using a notification object, returning the 38 endpoint number. */ 39 40 unsigned int NotificationSupport::subscribe(l4_cap_idx_t notifier, notify_flags_t flags) 41 { 42 std::lock_guard<std::mutex> guard(_lock); 43 44 unsigned int endpoint = _endpoints.size(); 45 46 _endpoints.resize(endpoint + 1); 47 48 /* Propagate deferred flags for new endpoints. */ 49 50 if (_deferred) 51 _endpoints[endpoint].deferred = _deferred; 52 53 _subscribe(endpoint, notifier, flags); 54 55 return endpoint; 56 } 57 58 /* Subscribe to a specific endpoint's notifications using a notification 59 object. */ 60 61 void NotificationSupport::subscribe(unsigned int endpoint, l4_cap_idx_t notifier, notify_flags_t flags) 62 { 63 std::lock_guard<std::mutex> guard(_lock); 64 65 if (endpoint >= _endpoints.size()) 66 { 67 _endpoints.resize(endpoint + 1); 68 69 /* Propagate deferred flags for new endpoints. */ 70 71 if (_deferred) 72 _endpoints[endpoint].deferred = _deferred; 73 } 74 75 _subscribe(endpoint, notifier, flags); 76 } 77 78 /* Common subscription functionality. */ 79 80 void NotificationSupport::_subscribe(unsigned int endpoint, l4_cap_idx_t notifier, notify_flags_t flags) 81 { 82 NotificationEndpoint &ep = _endpoints[endpoint]; 83 84 ep.notifiers.insert(notifier); 85 ep.flags = flags; 86 87 /* Send deferred conditions held from before subscription occurred. */ 88 89 if (ep.deferred) 90 { 91 _notify(endpoint, ep.deferred); 92 ep.deferred = 0; 93 } 94 } 95 96 /* Unsubscribe from an endpoint's notifications. */ 97 98 void NotificationSupport::unsubscribe(unsigned int endpoint, l4_cap_idx_t notifier) 99 { 100 std::lock_guard<std::mutex> guard(_lock); 101 102 if (endpoint >= _endpoints.size()) 103 return; 104 105 NotificationEndpoint &ep = _endpoints[endpoint]; 106 NotifierSet::iterator it = ep.notifiers.find(notifier); 107 108 if (it != ep.notifiers.end()) 109 { 110 ep.notifiers.erase(it); 111 ipc_cap_free_um(notifier); 112 113 if (ep.notifiers.empty()) 114 { 115 ep.flags = 0; 116 ep.deferred = 0; 117 } 118 } 119 } 120 121 /* Notify a particular endpoint. */ 122 123 void NotificationSupport::notify(unsigned int endpoint, notify_flags_t flags) 124 { 125 std::lock_guard<std::mutex> guard(_lock); 126 127 _notify(endpoint, flags); 128 } 129 130 void NotificationSupport::_notify(unsigned int endpoint, notify_flags_t flags) 131 { 132 if (endpoint >= _endpoints.size()) 133 return; 134 135 NotificationEndpoint &ep = _endpoints[endpoint]; 136 137 /* Notify the endpoint or hold any notification for potential future 138 subscription. */ 139 140 if (!ep.notifiers.empty()) 141 { 142 if (flags & ep.flags) 143 { 144 NotifierSet::iterator it; 145 146 for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++) 147 { 148 client_Notifier notifier(*it); 149 150 notifier.notify(flags & ep.flags); 151 } 152 } 153 } 154 else 155 ep.deferred = flags; 156 } 157 158 /* Notify the other endpoints. */ 159 160 void NotificationSupport::notify_others(unsigned int endpoint, notify_flags_t flags) 161 { 162 std::lock_guard<std::mutex> guard(_lock); 163 164 for (unsigned int i = 0; i < _endpoints.size(); i++) 165 if (i != endpoint) 166 _notify(i, flags); 167 168 _deferred |= flags; 169 } 170 171 /* Release notifiers for each endpoint. */ 172 173 void NotificationSupport::release_notifiers() 174 { 175 std::lock_guard<std::mutex> guard(_lock); 176 177 for (unsigned int endpoint = 0; endpoint < _endpoints.size(); endpoint++) 178 { 179 NotificationEndpoint &ep = _endpoints[endpoint]; 180 NotifierSet::iterator it; 181 182 for (it = ep.notifiers.begin(); it != ep.notifiers.end(); it++) 183 ipc_cap_free_um(*it); 184 185 ep.notifiers.clear(); 186 ep.flags = 0; 187 ep.deferred = 0; 188 } 189 } 190 191 // vim: tabstop=4 expandtab shiftwidth=4