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
Line 
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
33#include <adt/hash_table.h>
34#include <assert.h>
35#include <async.h>
36#include <errno.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include "service.h"
40#include "ns.h"
41
42/** Service hash table item. */
43typedef struct {
44 ht_link_t link;
45
46 /** Service ID */
47 service_t service;
48
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
63 /** Session to the service */
64 async_sess_t *sess;
65} hashed_iface_t;
66
67static size_t service_key_hash(const void *key)
68{
69 const service_t *srv = key;
70 return *srv;
71}
72
73static size_t service_hash(const ht_link_t *item)
74{
75 hashed_service_t *service =
76 hash_table_get_inst(item, hashed_service_t, link);
77
78 return service->service;
79}
80
81static bool service_key_equal(const void *key, size_t hash,
82 const ht_link_t *item)
83{
84 const service_t *srv = key;
85 hashed_service_t *service =
86 hash_table_get_inst(item, hashed_service_t, link);
87
88 return service->service == *srv;
89}
90
91static size_t iface_key_hash(const void *key)
92{
93 const iface_t *iface = key;
94 return *iface;
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
105static bool iface_key_equal(const void *key, size_t hash, const ht_link_t *item)
106{
107 const iface_t *kiface = key;
108 hashed_iface_t *iface =
109 hash_table_get_inst(item, hashed_iface_t, link);
110
111 return iface->iface == *kiface;
112}
113
114/** Operations for service hash table. */
115static const hash_table_ops_t service_hash_table_ops = {
116 .hash = service_hash,
117 .key_hash = service_key_hash,
118 .key_equal = service_key_equal,
119 .equal = NULL,
120 .remove_callback = NULL
121};
122
123/** Operations for interface hash table. */
124static const hash_table_ops_t iface_hash_table_ops = {
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
132/** Service hash table structure. */
133static hash_table_t service_hash_table;
134
135/** Pending connection structure. */
136typedef struct {
137 link_t link;
138 service_t service; /**< Service ID */
139 iface_t iface; /**< Interface ID */
140 ipc_call_t call; /**< Call waiting for the connection */
141} pending_conn_t;
142
143static list_t pending_conn;
144
145errno_t ns_service_init(void)
146{
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);
150 return ENOMEM;
151 }
152
153 list_initialize(&pending_conn);
154
155 return EOK;
156}
157
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);
161 async_forward_1(call, exch, iface, ipc_get_arg3(call), IPC_FF_NONE);
162 async_exchange_end(exch);
163}
164
165/** Process pending connection requests */
166void ns_pending_conn_process(void)
167{
168loop:
169 list_foreach(pending_conn, link, pending_conn_t, pending) {
170 ht_link_t *link =
171 hash_table_find(&service_hash_table, &pending->service);
172 if (!link)
173 continue;
174
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);
198
199 list_remove(&pending->link);
200 free(pending);
201
202 goto loop;
203 }
204}
205
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
257/** Register service.
258 *
259 * @param service Service to be registered.
260 * @param iface Interface to be registered.
261 *
262 * @return Zero on success or a value from @ref errno.h.
263 *
264 */
265errno_t ns_service_register(service_t service, iface_t iface)
266{
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 }
277
278 hashed_service_t *hashed_service =
279 (hashed_service_t *) malloc(sizeof(hashed_service_t));
280 if (!hashed_service)
281 return ENOMEM;
282
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;
290 hashed_service->service = service;
291 errno_t rc = ns_iface_register(hashed_service, iface);
292 if (rc != EOK) {
293 free(hashed_service);
294 return rc;
295 }
296
297 hash_table_insert(&service_hash_table, &hashed_service->link);
298 return EOK;
299}
300
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
362/** Connect client to service.
363 *
364 * @param service Service to be connected to.
365 * @param iface Interface to be connected to.
366 * @param call Pointer to call structure.
367 *
368 * @return Zero on success or a value from @ref errno.h.
369 *
370 */
371void ns_service_forward(service_t service, iface_t iface, ipc_call_t *call)
372{
373 sysarg_t flags = ipc_get_arg4(call);
374 errno_t retval;
375
376 ht_link_t *link = hash_table_find(&service_hash_table, &service);
377 if (!link) {
378 if (flags & IPC_FLAG_BLOCKING) {
379 /* Blocking connection, add to pending list */
380 errno_t rc = ns_pending_conn_add(service, iface, call);
381 if (rc == EOK)
382 return;
383
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);
394
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);
399 return;
400 }
401
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
412 retval = ENOENT;
413 goto out;
414 }
415
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);
420 return;
421
422out:
423 async_answer_0(call, retval);
424}
425
426/**
427 * @}
428 */
Note: See TracBrowser for help on using the repository browser.