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