source: mainline/uspace/srv/ns/service.c

Last change on this file was 0db0df2, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 3 months ago

Hash table improvements

Implement hash_table_foreach macro, analogous to list_foreach.

Remove superfluous argument to hash_table_find_next().
(If the user needs to recheck the part of the list already
checked by hash_table_find(), they can just rerun that function.)

Add hash argument to hash_table_ops_t::key_equal.
The big change here is that users with big keys can store the hash
value alongside key in their entries, and for the low low cost of
sizeof(size_t) bytes eliminate a bunch of expensive key comparisons.

Also added a hash function for strings and arbitrary data.
Found this one by asking ChatGPT, because the latency of accesses
to my book collection is currently a couple of hours.

+ Some drive-by unused #include removal.

  • Property mode set to 100644
File size: 10.2 KB
RevLine 
[40313e4]1/*
2 * Copyright (c) 2009 Martin Decky
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup ns
30 * @{
31 */
32
[d9c8c81]33#include <adt/hash_table.h>
[40313e4]34#include <assert.h>
[7b616e2]35#include <async.h>
[40313e4]36#include <errno.h>
[c7bbf029]37#include <stdio.h>
[38d150e]38#include <stdlib.h>
[40313e4]39#include "service.h"
40#include "ns.h"
41
42/** Service hash table item. */
43typedef struct {
[062d900]44 ht_link_t link;
[a35b458]45
[9cfbf2f]46 /** Service ID */
47 service_t service;
[a35b458]48
[9b1baac]49 /** Interface hash table */
50 hash_table_t iface_hash_table;
51
52 /** Broker session to the service */
53 async_sess_t *broker_sess;
54} hashed_service_t;
55
56/** Interface hash table item. */
57typedef struct {
58 ht_link_t link;
59
60 /** Interface ID */
61 iface_t iface;
62
[7b616e2]63 /** Session to the service */
64 async_sess_t *sess;
[9b1baac]65} hashed_iface_t;
[40313e4]66
[5e801dc]67static size_t service_key_hash(const void *key)
[40313e4]68{
[5e801dc]69 const service_t *srv = key;
70 return *srv;
[40313e4]71}
72
[062d900]73static size_t service_hash(const ht_link_t *item)
[40313e4]74{
[9cfbf2f]75 hashed_service_t *service =
76 hash_table_get_inst(item, hashed_service_t, link);
[a35b458]77
[9cfbf2f]78 return service->service;
[40313e4]79}
80
[0db0df2]81static bool service_key_equal(const void *key, size_t hash,
82 const ht_link_t *item)
[40313e4]83{
[5e801dc]84 const service_t *srv = key;
[9cfbf2f]85 hashed_service_t *service =
86 hash_table_get_inst(item, hashed_service_t, link);
[a35b458]87
[5e801dc]88 return service->service == *srv;
[40313e4]89}
90
[5e801dc]91static size_t iface_key_hash(const void *key)
[9b1baac]92{
[5e801dc]93 const iface_t *iface = key;
94 return *iface;
[9b1baac]95}
96
97static size_t iface_hash(const ht_link_t *item)
98{
99 hashed_iface_t *iface =
100 hash_table_get_inst(item, hashed_iface_t, link);
101
102 return iface->iface;
103}
104
[0db0df2]105static bool iface_key_equal(const void *key, size_t hash, const ht_link_t *item)
[9b1baac]106{
[5e801dc]107 const iface_t *kiface = key;
[9b1baac]108 hashed_iface_t *iface =
109 hash_table_get_inst(item, hashed_iface_t, link);
110
[5e801dc]111 return iface->iface == *kiface;
[9b1baac]112}
113
[40313e4]114/** Operations for service hash table. */
[61eb2ce2]115static const hash_table_ops_t service_hash_table_ops = {
[40313e4]116 .hash = service_hash,
[062d900]117 .key_hash = service_key_hash,
118 .key_equal = service_key_equal,
[4e00f87]119 .equal = NULL,
120 .remove_callback = NULL
[40313e4]121};
122
[9b1baac]123/** Operations for interface hash table. */
[61eb2ce2]124static const hash_table_ops_t iface_hash_table_ops = {
[9b1baac]125 .hash = iface_hash,
126 .key_hash = iface_key_hash,
127 .key_equal = iface_key_equal,
128 .equal = NULL,
129 .remove_callback = NULL
130};
131
[40313e4]132/** Service hash table structure. */
133static hash_table_t service_hash_table;
134
135/** Pending connection structure. */
136typedef struct {
137 link_t link;
[a46e56b]138 service_t service; /**< Service ID */
139 iface_t iface; /**< Interface ID */
[984a9ba]140 ipc_call_t call; /**< Call waiting for the connection */
[40313e4]141} pending_conn_t;
142
[b72efe8]143static list_t pending_conn;
[40313e4]144
[9b1baac]145errno_t ns_service_init(void)
[40313e4]146{
[9cfbf2f]147 if (!hash_table_create(&service_hash_table, 0, 0,
148 &service_hash_table_ops)) {
149 printf("%s: No memory available for services\n", NAME);
[40313e4]150 return ENOMEM;
151 }
[a35b458]152
[40313e4]153 list_initialize(&pending_conn);
[a35b458]154
[40313e4]155 return EOK;
156}
157
[9b1baac]158static void ns_forward(async_sess_t *sess, ipc_call_t *call, iface_t iface)
159{
160 async_exch_t *exch = async_exchange_begin(sess);
[fafb8e5]161 async_forward_1(call, exch, iface, ipc_get_arg3(call), IPC_FF_NONE);
[9b1baac]162 async_exchange_end(exch);
163}
164
[40313e4]165/** Process pending connection requests */
[9b1baac]166void ns_pending_conn_process(void)
[40313e4]167{
168loop:
[9cfbf2f]169 list_foreach(pending_conn, link, pending_conn_t, pending) {
[9b1baac]170 ht_link_t *link =
171 hash_table_find(&service_hash_table, &pending->service);
[40313e4]172 if (!link)
173 continue;
[a35b458]174
[9b1baac]175 hashed_service_t *hashed_service =
176 hash_table_get_inst(link, hashed_service_t, link);
177
178 link = hash_table_find(&hashed_service->iface_hash_table,
179 &pending->iface);
180 if (!link) {
181 if (hashed_service->broker_sess != NULL) {
182 ns_forward(hashed_service->broker_sess, &pending->call,
183 pending->iface);
184
185 list_remove(&pending->link);
186 free(pending);
187
188 goto loop;
189 }
190
191 continue;
192 }
193
194 hashed_iface_t *hashed_iface =
195 hash_table_get_inst(link, hashed_iface_t, link);
196
197 ns_forward(hashed_iface->sess, &pending->call, pending->iface);
[a35b458]198
[9cfbf2f]199 list_remove(&pending->link);
200 free(pending);
[a35b458]201
[40313e4]202 goto loop;
203 }
204}
205
[9b1baac]206/** Register interface to a service.
207 *
208 * @param service Service to which the interface belongs.
209 * @param iface Interface to be registered.
210 *
211 * @return Zero on success or a value from @ref errno.h.
212 *
213 */
214static errno_t ns_iface_register(hashed_service_t *hashed_service, iface_t iface)
215{
216 ht_link_t *link = hash_table_find(&hashed_service->iface_hash_table,
217 &iface);
218 if (link)
219 return EEXIST;
220
221 hashed_iface_t *hashed_iface =
222 (hashed_iface_t *) malloc(sizeof(hashed_iface_t));
223 if (!hashed_iface)
224 return ENOMEM;
225
226 hashed_iface->iface = iface;
227 hashed_iface->sess = async_callback_receive(EXCHANGE_SERIALIZE);
228 if (hashed_iface->sess == NULL) {
229 free(hashed_iface);
230 return EIO;
231 }
232
233 hash_table_insert(&hashed_service->iface_hash_table,
234 &hashed_iface->link);
235 return EOK;
236}
237
238/** Register broker to a service.
239 *
240 * @param service Service to which the broker belongs.
241 *
242 * @return Zero on success or a value from @ref errno.h.
243 *
244 */
245static errno_t ns_broker_register(hashed_service_t *hashed_service)
246{
247 if (hashed_service->broker_sess != NULL)
248 return EEXIST;
249
250 hashed_service->broker_sess = async_callback_receive(EXCHANGE_SERIALIZE);
251 if (hashed_service->broker_sess == NULL)
252 return EIO;
253
254 return EOK;
255}
256
[40313e4]257/** Register service.
258 *
259 * @param service Service to be registered.
[9b1baac]260 * @param iface Interface to be registered.
[40313e4]261 *
262 * @return Zero on success or a value from @ref errno.h.
263 *
264 */
[9b1baac]265errno_t ns_service_register(service_t service, iface_t iface)
[40313e4]266{
[9b1baac]267 ht_link_t *link = hash_table_find(&service_hash_table, &service);
268
269 if (link) {
270 hashed_service_t *hashed_service =
271 hash_table_get_inst(link, hashed_service_t, link);
272
273 assert(hashed_service->service == service);
274
275 return ns_iface_register(hashed_service, iface);
276 }
[a35b458]277
[9cfbf2f]278 hashed_service_t *hashed_service =
279 (hashed_service_t *) malloc(sizeof(hashed_service_t));
280 if (!hashed_service)
[40313e4]281 return ENOMEM;
[a35b458]282
[9b1baac]283 if (!hash_table_create(&hashed_service->iface_hash_table, 0, 0,
284 &iface_hash_table_ops)) {
285 free(hashed_service);
286 return ENOMEM;
287 }
288
289 hashed_service->broker_sess = NULL;
[9cfbf2f]290 hashed_service->service = service;
[9b1baac]291 errno_t rc = ns_iface_register(hashed_service, iface);
292 if (rc != EOK) {
293 free(hashed_service);
294 return rc;
295 }
[a35b458]296
[9cfbf2f]297 hash_table_insert(&service_hash_table, &hashed_service->link);
[007e6efa]298 return EOK;
[40313e4]299}
300
[9b1baac]301/** Register broker service.
302 *
303 * @param service Broker service to be registered.
304 *
305 * @return Zero on success or a value from @ref errno.h.
306 *
307 */
308errno_t ns_service_register_broker(service_t service)
309{
310 ht_link_t *link = hash_table_find(&service_hash_table, &service);
311
312 if (link) {
313 hashed_service_t *hashed_service =
314 hash_table_get_inst(link, hashed_service_t, link);
315
316 assert(hashed_service->service == service);
317
318 return ns_broker_register(hashed_service);
319 }
320
321 hashed_service_t *hashed_service =
322 (hashed_service_t *) malloc(sizeof(hashed_service_t));
323 if (!hashed_service)
324 return ENOMEM;
325
326 if (!hash_table_create(&hashed_service->iface_hash_table, 0, 0,
327 &iface_hash_table_ops)) {
328 free(hashed_service);
329 return ENOMEM;
330 }
331
332 hashed_service->broker_sess = NULL;
333 hashed_service->service = service;
334 errno_t rc = ns_broker_register(hashed_service);
335 if (rc != EOK) {
336 free(hashed_service);
337 return rc;
338 }
339
340 hash_table_insert(&service_hash_table, &hashed_service->link);
341 return EOK;
342}
343
344/** Add pending connection */
345static errno_t ns_pending_conn_add(service_t service, iface_t iface,
346 ipc_call_t *call)
347{
348 pending_conn_t *pending =
349 (pending_conn_t *) malloc(sizeof(pending_conn_t));
350 if (!pending)
351 return ENOMEM;
352
353 link_initialize(&pending->link);
354 pending->service = service;
355 pending->iface = iface;
356 pending->call = *call;
357
358 list_append(&pending->link, &pending_conn);
359 return EOK;
360}
361
[40313e4]362/** Connect client to service.
363 *
[eed4139]364 * @param service Service to be connected to.
365 * @param iface Interface to be connected to.
366 * @param call Pointer to call structure.
[40313e4]367 *
368 * @return Zero on success or a value from @ref errno.h.
369 *
370 */
[9b1baac]371void ns_service_forward(service_t service, iface_t iface, ipc_call_t *call)
[40313e4]372{
[fafb8e5]373 sysarg_t flags = ipc_get_arg4(call);
[b7fd2a0]374 errno_t retval;
[a35b458]375
[062d900]376 ht_link_t *link = hash_table_find(&service_hash_table, &service);
[40313e4]377 if (!link) {
[9cfbf2f]378 if (flags & IPC_FLAG_BLOCKING) {
[40313e4]379 /* Blocking connection, add to pending list */
[9b1baac]380 errno_t rc = ns_pending_conn_add(service, iface, call);
381 if (rc == EOK)
382 return;
[a35b458]383
[9b1baac]384 retval = rc;
385 goto out;
386 }
387
388 retval = ENOENT;
389 goto out;
390 }
391
392 hashed_service_t *hashed_service =
393 hash_table_get_inst(link, hashed_service_t, link);
[a35b458]394
[9b1baac]395 link = hash_table_find(&hashed_service->iface_hash_table, &iface);
396 if (!link) {
397 if (hashed_service->broker_sess != NULL) {
398 ns_forward(hashed_service->broker_sess, call, iface);
[40313e4]399 return;
400 }
[a35b458]401
[9b1baac]402 if (flags & IPC_FLAG_BLOCKING) {
403 /* Blocking connection, add to pending list */
404 errno_t rc = ns_pending_conn_add(service, iface, call);
405 if (rc == EOK)
406 return;
407
408 retval = rc;
409 goto out;
410 }
411
[40313e4]412 retval = ENOENT;
413 goto out;
414 }
[a35b458]415
[9b1baac]416 hashed_iface_t *hashed_iface =
417 hash_table_get_inst(link, hashed_iface_t, link);
418
419 ns_forward(hashed_iface->sess, call, iface);
[c638c07]420 return;
[a35b458]421
[40313e4]422out:
[984a9ba]423 async_answer_0(call, retval);
[40313e4]424}
425
426/**
427 * @}
428 */
Note: See TracBrowser for help on using the repository browser.