@@ -204,28 +204,24 @@ void webserver::register_resource(const std::string& resource,
204204// 1. Validates the handler, the method set (non-empty, no count_
205205void webserver::unregister_impl_ (const string& resource, bool family) {
206206 detail::http_endpoint he (resource, family, true , regex_checking);
207- std::unique_lock registered_resources_lock (impl_->registered_resources_mutex );
208207
209208 {
210- std::lock_guard<std::mutex> cache_lock (impl_->route_cache_mutex );
211- impl_->route_cache_list .clear ();
212- impl_->route_cache_map .clear ();
213- }
214-
215- impl_->registered_resources .erase (he);
216- impl_->registered_resources_regex .erase (he);
217- // The string-keyed fast-path map only ever holds exact (non-family)
218- // entries (see register_impl_). A prefix unregister has nothing to
219- // do here.
220- if (!family) {
221- impl_->registered_resources_str .erase (he.get_url_complete ());
209+ std::unique_lock registered_resources_lock (impl_->registered_resources_mutex );
210+ impl_->registered_resources .erase (he);
211+ impl_->registered_resources_regex .erase (he);
212+ // The string-keyed fast-path map only ever holds exact (non-family)
213+ // entries (see register_impl_). A prefix unregister has nothing to
214+ // do here.
215+ if (!family) {
216+ impl_->registered_resources_str .erase (he.get_url_complete ());
217+ }
222218 }
223219
224220 // TASK-027: mirror the erasure into the v2 3-tier table. Lock order:
225- // we already hold registered_resources_lock (v1 table) ; take
226- // route_table_mutex_ next, then route_cache_mutex_ via the
227- // route_cache::clear() helper. The discipline (table BEFORE cache)
228- // is consistent with register_impl_ and the documented invariant .
221+ // registered_resources_mutex released above ; take route_table_mutex_
222+ // next, then invalidate_route_cache() takes route_cache_mutex_.
223+ // This matches the pattern used in register_impl_ / on_methods_:
224+ // table lock released before cache is cleared .
229225 {
230226 std::unique_lock table_lock (impl_->route_table_mutex_ );
231227 const std::string& key = he.get_url_complete ();
@@ -245,7 +241,7 @@ void webserver::unregister_impl_(const string& resource, bool family) {
245241 impl_->regex_routes_ .end ());
246242 }
247243 }
248- impl_->route_cache_v2 . clear ();
244+ impl_->invalidate_route_cache ();
249245}
250246
251247void webserver::unregister_path (const string& path) {
@@ -261,24 +257,19 @@ void webserver::unregister_resource(const string& resource) {
261257 detail::http_endpoint he_exact (resource, /* family=*/ false , true , regex_checking);
262258 detail::http_endpoint he_prefix (resource, /* family=*/ true , true , regex_checking);
263259
264- // Hold a single write-lock across both erasures so no request thread can
265- // observe a partially-unregistered state (CWE-367 TOCTOU fix: the exact
266- // entry and the prefix entry are removed atomically).
267- std::unique_lock registered_resources_lock (impl_->registered_resources_mutex );
268-
269260 {
270- std::lock_guard<std::mutex> cache_lock (impl_->route_cache_mutex );
271- impl_->route_cache_list .clear ();
272- impl_->route_cache_map .clear ();
261+ // Hold a single write-lock across both v1 erasures so no request thread
262+ // can observe a partially-unregistered state (CWE-367 TOCTOU fix: the
263+ // exact entry and the prefix entry are removed atomically).
264+ std::unique_lock registered_resources_lock (impl_->registered_resources_mutex );
265+ impl_->registered_resources .erase (he_exact);
266+ impl_->registered_resources .erase (he_prefix);
267+ impl_->registered_resources_regex .erase (he_exact);
268+ impl_->registered_resources_regex .erase (he_prefix);
269+ // The string-keyed fast-path map only holds exact (non-family) entries.
270+ impl_->registered_resources_str .erase (he_exact.get_url_complete ());
273271 }
274272
275- impl_->registered_resources .erase (he_exact);
276- impl_->registered_resources .erase (he_prefix);
277- impl_->registered_resources_regex .erase (he_exact);
278- impl_->registered_resources_regex .erase (he_prefix);
279- // The string-keyed fast-path map only holds exact (non-family) entries.
280- impl_->registered_resources_str .erase (he_exact.get_url_complete ());
281-
282273 // TASK-027: mirror into the v2 3-tier table. Erase under both
283274 // classifications so a prior register_path AND register_prefix on
284275 // the same path are both cleared atomically.
@@ -297,7 +288,10 @@ void webserver::unregister_resource(const string& resource) {
297288 }),
298289 impl_->regex_routes_ .end ());
299290 }
300- impl_->route_cache_v2 .clear ();
291+ // Delegate cache clearing to invalidate_route_cache() matching the
292+ // pattern used by register_impl_ and on_methods_ (table lock released
293+ // before cache is cleared).
294+ impl_->invalidate_route_cache ();
301295}
302296
303297// TASK-029: The v2.0 public IP-control API is the pair block_ip / unblock_ip.
0 commit comments