1 /*
2 * %CopyrightBegin%
3 *
4 * Copyright Ericsson AB 1996-2018. All Rights Reserved.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 * %CopyrightEnd%
19 */
20
21 /*
22 * Manage registered processes.
23 */
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #include "sys.h"
29 #include "erl_vm.h"
30 #include "global.h"
31 #include "hash.h"
32 #include "atom.h"
33 #include "register.h"
34
35 static Hash process_reg;
36
37 #define PREG_HASH_SIZE 10
38
39 #define REG_HASH(term) ((HashValue) atom_val(term))
40
41 static erts_rwmtx_t regtab_rwmtx;
42
43 #define reg_try_read_lock() erts_rwmtx_tryrlock(®tab_rwmtx)
44 #define reg_try_write_lock() erts_rwmtx_tryrwlock(®tab_rwmtx)
45 #define reg_read_lock() erts_rwmtx_rlock(®tab_rwmtx)
46 #define reg_write_lock() erts_rwmtx_rwlock(®tab_rwmtx)
47 #define reg_read_unlock() erts_rwmtx_runlock(®tab_rwmtx)
48 #define reg_write_unlock() erts_rwmtx_rwunlock(®tab_rwmtx)
49
50 static ERTS_INLINE void
reg_safe_read_lock(Process * c_p,ErtsProcLocks * c_p_locks)51 reg_safe_read_lock(Process *c_p, ErtsProcLocks *c_p_locks)
52 {
53 if (*c_p_locks) {
54 ASSERT(c_p);
55 ASSERT(c_p_locks);
56 ASSERT(*c_p_locks);
57
58 if (reg_try_read_lock() != EBUSY) {
59 #ifdef ERTS_ENABLE_LOCK_CHECK
60 erts_proc_lc_might_unlock(c_p, *c_p_locks);
61 #endif
62 return;
63 }
64
65 /* Release process locks in order to avoid deadlock */
66 erts_proc_unlock(c_p, *c_p_locks);
67 *c_p_locks = 0;
68 }
69
70 reg_read_lock();
71 }
72
73 static ERTS_INLINE void
reg_safe_write_lock(Process * c_p,ErtsProcLocks * c_p_locks)74 reg_safe_write_lock(Process *c_p, ErtsProcLocks *c_p_locks)
75 {
76 if (*c_p_locks) {
77 ASSERT(c_p);
78 ASSERT(c_p_locks);
79 ASSERT(*c_p_locks);
80
81 if (reg_try_write_lock() != EBUSY) {
82 #ifdef ERTS_ENABLE_LOCK_CHECK
83 erts_proc_lc_might_unlock(c_p, *c_p_locks);
84 #endif
85 return;
86 }
87
88 /* Release process locks in order to avoid deadlock */
89 erts_proc_unlock(c_p, *c_p_locks);
90 *c_p_locks = 0;
91 }
92
93 reg_write_lock();
94 }
95
96
97 static ERTS_INLINE int
is_proc_alive(Process * p)98 is_proc_alive(Process *p)
99 {
100 return !ERTS_PROC_IS_EXITING(p);
101 }
102
register_info(fmtfn_t to,void * to_arg)103 void register_info(fmtfn_t to, void *to_arg)
104 {
105 int lock = !ERTS_IS_CRASH_DUMPING;
106 if (lock)
107 reg_read_lock();
108 hash_info(to, to_arg, &process_reg);
109 if (lock)
110 reg_read_unlock();
111 }
112
reg_hash(RegProc * obj)113 static HashValue reg_hash(RegProc* obj)
114 {
115 return REG_HASH(obj->name);
116 }
117
reg_cmp(RegProc * tmpl,RegProc * obj)118 static int reg_cmp(RegProc *tmpl, RegProc *obj) {
119 return tmpl->name != obj->name;
120 }
121
reg_alloc(RegProc * tmpl)122 static RegProc* reg_alloc(RegProc *tmpl)
123 {
124 RegProc* obj = (RegProc*) erts_alloc(ERTS_ALC_T_REG_PROC, sizeof(RegProc));
125 if (!obj) {
126 erts_exit(ERTS_ERROR_EXIT, "Can't allocate %d bytes of memory\n", sizeof(RegProc));
127 }
128 obj->name = tmpl->name;
129 obj->p = tmpl->p;
130 obj->pt = tmpl->pt;
131 return obj;
132 }
133
reg_free(RegProc * obj)134 static void reg_free(RegProc *obj)
135 {
136 erts_free(ERTS_ALC_T_REG_PROC, (void*) obj);
137 }
138
init_register_table(void)139 void init_register_table(void)
140 {
141 HashFunctions f;
142 erts_rwmtx_opt_t rwmtx_opt = ERTS_RWMTX_OPT_DEFAULT_INITER;
143 rwmtx_opt.type = ERTS_RWMTX_TYPE_FREQUENT_READ;
144 rwmtx_opt.lived = ERTS_RWMTX_LONG_LIVED;
145
146 erts_rwmtx_init_opt(®tab_rwmtx, &rwmtx_opt, "reg_tab", NIL,
147 ERTS_LOCK_FLAGS_PROPERTY_STATIC | ERTS_LOCK_FLAGS_CATEGORY_GENERIC);
148
149 f.hash = (H_FUN) reg_hash;
150 f.cmp = (HCMP_FUN) reg_cmp;
151 f.alloc = (HALLOC_FUN) reg_alloc;
152 f.free = (HFREE_FUN) reg_free;
153 f.meta_alloc = (HMALLOC_FUN) erts_alloc;
154 f.meta_free = (HMFREE_FUN) erts_free;
155 f.meta_print = (HMPRINT_FUN) erts_print;
156
157 hash_init(ERTS_ALC_T_REG_TABLE, &process_reg, "process_reg",
158 PREG_HASH_SIZE, f);
159 }
160
161 /*
162 * Register a process or port (can't be registered twice).
163 * Returns 0 if name, process or port is already registered.
164 *
165 * When smp support is enabled:
166 * * Assumes that main lock is locked (and only main lock)
167 * on c_p.
168 *
169 */
erts_register_name(Process * c_p,Eterm name,Eterm id)170 int erts_register_name(Process *c_p, Eterm name, Eterm id)
171 {
172 int res = 0;
173 Process *proc = NULL;
174 Port *port = NULL;
175 RegProc r, *rp;
176 ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(c_p);
177
178 if (is_not_atom(name) || name == am_undefined)
179 return res;
180
181 if (c_p->common.id == id) /* A very common case I think... */
182 proc = c_p;
183 else {
184 if (is_not_internal_pid(id) && is_not_internal_port(id))
185 return res;
186 erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
187 if (is_internal_port(id)) {
188 port = erts_id2port(id);
189 if (!port)
190 goto done;
191 }
192 }
193
194 {
195 ErtsProcLocks proc_locks = proc ? ERTS_PROC_LOCK_MAIN : 0;
196 reg_safe_write_lock(proc, &proc_locks);
197
198 if (proc && !proc_locks)
199 erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
200 }
201
202 if (is_internal_pid(id)) {
203 if (!proc)
204 proc = erts_pid2proc(NULL, 0, id, ERTS_PROC_LOCK_MAIN);
205 r.p = proc;
206 if (!proc)
207 goto done;
208 if (proc->common.u.alive.reg)
209 goto done;
210 r.pt = NULL;
211 }
212 else {
213 ASSERT(!INVALID_PORT(port, id));
214 ERTS_LC_ASSERT(erts_lc_is_port_locked(port));
215 r.pt = port;
216 if (r.pt->common.u.alive.reg)
217 goto done;
218 r.p = NULL;
219 }
220
221 r.name = name;
222
223 rp = (RegProc*) hash_put(&process_reg, (void*) &r);
224 if (proc && rp->p == proc) {
225 if (IS_TRACED_FL(proc, F_TRACE_PROCS)) {
226 trace_proc(proc, ERTS_PROC_LOCK_MAIN,
227 proc, am_register, name);
228 }
229 proc->common.u.alive.reg = rp;
230 }
231 else if (port && rp->pt == port) {
232 if (IS_TRACED_FL(port, F_TRACE_PORTS)) {
233 trace_port(port, am_register, name);
234 }
235 port->common.u.alive.reg = rp;
236 }
237
238 if ((rp->p && rp->p->common.id == id)
239 || (rp->pt && rp->pt->common.id == id)) {
240 res = 1;
241 }
242
243 done:
244 reg_write_unlock();
245 if (port)
246 erts_port_release(port);
247 if (c_p != proc) {
248 if (proc)
249 erts_proc_unlock(proc, ERTS_PROC_LOCK_MAIN);
250 erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
251 }
252 return res;
253 }
254
255 /*
256 *
257 * When smp support is enabled:
258 * * Assumes that main lock is locked (and only main lock)
259 * on c_p.
260 *
261 * * am_undefined is returned if c_p became exiting.
262 */
263
264 Eterm
erts_whereis_name_to_id(Process * c_p,Eterm name)265 erts_whereis_name_to_id(Process *c_p, Eterm name)
266 {
267 Eterm res = am_undefined;
268 HashValue hval;
269 int ix;
270 HashBucket* b;
271 ErtsProcLocks c_p_locks = 0;
272 if (c_p) {
273 c_p_locks = ERTS_PROC_LOCK_MAIN;
274 ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(c_p);
275 }
276 reg_safe_read_lock(c_p, &c_p_locks);
277
278 if (c_p && !c_p_locks)
279 erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
280
281 hval = REG_HASH(name);
282 ix = hval % process_reg.size;
283 b = process_reg.bucket[ix];
284
285 /*
286 * Note: We have inlined the code from hash.c for speed.
287 */
288
289 while (b) {
290 RegProc* rp = (RegProc *) b;
291 if (rp->name == name) {
292 /*
293 * SMP NOTE: No need to lock registered entity since it cannot
294 * be removed without acquiring write reg lock and id on entity
295 * is read only.
296 */
297 if (rp->p)
298 res = rp->p->common.id;
299 else if (rp->pt)
300 res = rp->pt->common.id;
301 break;
302 }
303 b = b->next;
304 }
305
306 reg_read_unlock();
307
308 ASSERT(is_internal_pid(res) || is_internal_port(res) || res==am_undefined);
309
310 return res;
311 }
312
313
314 void
erts_whereis_name(Process * c_p,ErtsProcLocks c_p_locks,Eterm name,Process ** proc,ErtsProcLocks need_locks,int flags,Port ** port,int lock_port)315 erts_whereis_name(Process *c_p,
316 ErtsProcLocks c_p_locks,
317 Eterm name,
318 Process** proc,
319 ErtsProcLocks need_locks,
320 int flags,
321 Port** port,
322 int lock_port)
323 {
324 RegProc* rp = NULL;
325 HashValue hval;
326 int ix;
327 HashBucket* b;
328 ErtsProcLocks current_c_p_locks;
329 Port *pending_port = NULL;
330
331 if (!c_p)
332 c_p_locks = 0;
333 current_c_p_locks = c_p_locks;
334
335 restart:
336
337 reg_safe_read_lock(c_p, ¤t_c_p_locks);
338
339 /* Locked locks:
340 * - port lock on pending_port if pending_port != NULL
341 * - read reg lock
342 * - current_c_p_locks (either c_p_locks or 0) on c_p
343 */
344
345 hval = REG_HASH(name);
346 ix = hval % process_reg.size;
347 b = process_reg.bucket[ix];
348
349 /*
350 * Note: We have inlined the code from hash.c for speed.
351 */
352
353 while (b) {
354 if (((RegProc *) b)->name == name) {
355 rp = (RegProc *) b;
356 break;
357 }
358 b = b->next;
359 }
360
361 if (proc) {
362 if (!rp)
363 *proc = NULL;
364 else {
365 if (!rp->p)
366 *proc = NULL;
367 else {
368 if (need_locks) {
369 erts_proc_safelock(c_p,
370 current_c_p_locks,
371 c_p_locks,
372 rp->p,
373 0,
374 need_locks);
375 current_c_p_locks = c_p_locks;
376 }
377 if ((flags & ERTS_P2P_FLG_ALLOW_OTHER_X) || is_proc_alive(rp->p))
378 *proc = rp->p;
379 else {
380 if (need_locks)
381 erts_proc_unlock(rp->p, need_locks);
382 *proc = NULL;
383 }
384 }
385 if (*proc && (flags & ERTS_P2P_FLG_INC_REFC))
386 erts_proc_inc_refc(*proc);
387 }
388 }
389
390 if (port) {
391 if (!rp || !rp->pt)
392 *port = NULL;
393 else {
394 if (lock_port) {
395 if (pending_port == rp->pt)
396 pending_port = NULL;
397 else {
398 if (pending_port) {
399 /* Ahh! Registered port changed while reg lock
400 was unlocked... */
401 erts_port_release(pending_port);
402 pending_port = NULL;
403 }
404
405 if (erts_port_trylock(rp->pt) == EBUSY) {
406 Eterm id = rp->pt->common.id; /* id read only... */
407 /* Unlock all locks, acquire port lock, and restart... */
408 if (current_c_p_locks) {
409 erts_proc_unlock(c_p, current_c_p_locks);
410 current_c_p_locks = 0;
411 }
412 reg_read_unlock();
413 pending_port = erts_id2port(id);
414 goto restart;
415 }
416 }
417 ERTS_LC_ASSERT(erts_lc_is_port_locked(rp->pt));
418 }
419 *port = rp->pt;
420 }
421 }
422
423 if (c_p && !current_c_p_locks)
424 erts_proc_lock(c_p, c_p_locks);
425 if (pending_port)
426 erts_port_release(pending_port);
427
428 reg_read_unlock();
429 }
430
431 Process *
erts_whereis_process(Process * c_p,ErtsProcLocks c_p_locks,Eterm name,ErtsProcLocks need_locks,int flags)432 erts_whereis_process(Process *c_p,
433 ErtsProcLocks c_p_locks,
434 Eterm name,
435 ErtsProcLocks need_locks,
436 int flags)
437 {
438 Process *proc;
439 erts_whereis_name(c_p, c_p_locks, name, &proc, need_locks, flags, NULL, 0);
440 return proc;
441 }
442
443
444 /*
445 * Unregister a name
446 * Return 0 if not registered
447 * Otherwise returns 1
448 *
449 */
erts_unregister_name(Process * c_p,ErtsProcLocks c_p_locks,Port * c_prt,Eterm name)450 int erts_unregister_name(Process *c_p,
451 ErtsProcLocks c_p_locks,
452 Port *c_prt,
453 Eterm name)
454 {
455 int res = 0;
456 RegProc r, *rp;
457 Port *port = c_prt;
458 ErtsProcLocks current_c_p_locks = 0;
459
460 /*
461 * SMP note: If 'c_prt != NULL' and 'c_prt->reg->name == name',
462 * we are *not* allowed to temporarily release the lock
463 * on c_prt.
464 */
465
466 if (!c_p) {
467 c_p_locks = 0;
468 }
469 current_c_p_locks = c_p_locks;
470
471 restart:
472
473 reg_safe_write_lock(c_p, ¤t_c_p_locks);
474
475 r.name = name;
476 if (is_non_value(name)) {
477 /* Unregister current process name */
478 ASSERT(c_p);
479 if (current_c_p_locks != c_p_locks) {
480 erts_proc_lock(c_p, c_p_locks);
481 current_c_p_locks = c_p_locks;
482 }
483 if (c_p->common.u.alive.reg) {
484 r.name = c_p->common.u.alive.reg->name;
485 } else {
486 /* Name got unregistered while main lock was released */
487 res = 0;
488 goto done;
489 }
490 }
491
492 if ((rp = (RegProc*) hash_get(&process_reg, (void*) &r)) != NULL) {
493 if (rp->pt) {
494 if (port != rp->pt) {
495 if (port) {
496 ASSERT(port != c_prt);
497 erts_port_release(port);
498 port = NULL;
499 }
500
501 if (erts_port_trylock(rp->pt) == EBUSY) {
502 Eterm id = rp->pt->common.id; /* id read only... */
503 /* Unlock all locks, acquire port lock, and restart... */
504 if (current_c_p_locks) {
505 erts_proc_unlock(c_p, current_c_p_locks);
506 current_c_p_locks = 0;
507 }
508 reg_write_unlock();
509 port = erts_id2port(id);
510 goto restart;
511 }
512 port = rp->pt;
513 }
514
515 ASSERT(rp->pt == port);
516 ERTS_LC_ASSERT(erts_lc_is_port_locked(port));
517
518 rp->pt->common.u.alive.reg = NULL;
519
520 if (IS_TRACED_FL(port, F_TRACE_PORTS)) {
521 if (current_c_p_locks) {
522 erts_proc_unlock(c_p, current_c_p_locks);
523 current_c_p_locks = 0;
524 }
525 trace_port(port, am_unregister, r.name);
526 }
527
528 } else if (rp->p) {
529
530 erts_proc_safelock(c_p,
531 current_c_p_locks,
532 c_p_locks,
533 rp->p,
534 (c_p == rp->p) ? current_c_p_locks : 0,
535 ERTS_PROC_LOCK_MAIN);
536 current_c_p_locks = c_p_locks;
537 rp->p->common.u.alive.reg = NULL;
538 if (IS_TRACED_FL(rp->p, F_TRACE_PROCS)) {
539 trace_proc(rp->p, (c_p == rp->p) ? c_p_locks : ERTS_PROC_LOCK_MAIN,
540 rp->p, am_unregister, r.name);
541 }
542 if (rp->p != c_p) {
543 erts_proc_unlock(rp->p, ERTS_PROC_LOCK_MAIN);
544 }
545 }
546 hash_erase(&process_reg, (void*) &r);
547 res = 1;
548 }
549
550 done:
551
552 reg_write_unlock();
553 if (c_prt != port) {
554 if (port) {
555 erts_port_release(port);
556 }
557 if (c_prt) {
558 erts_port_lock(c_prt);
559 }
560 }
561 if (c_p && !current_c_p_locks) {
562 erts_proc_lock(c_p, c_p_locks);
563 }
564 return res;
565 }
566
process_reg_size(void)567 int process_reg_size(void)
568 {
569 int size;
570 int lock = !ERTS_IS_CRASH_DUMPING;
571 if (lock)
572 reg_read_lock();
573 size = process_reg.size;
574 if (lock)
575 reg_read_unlock();
576 return size;
577 }
578
process_reg_sz(void)579 int process_reg_sz(void)
580 {
581 int sz;
582 int lock = !ERTS_IS_CRASH_DUMPING;
583 if (lock)
584 reg_read_lock();
585 sz = hash_table_sz(&process_reg);
586 if (lock)
587 reg_read_unlock();
588 return sz;
589 }
590
591 /**********************************************************************/
592
593 #include "bif.h"
594
595 /* return a list of the registered processes */
596
registered_0(BIF_ALIST_0)597 BIF_RETTYPE registered_0(BIF_ALIST_0)
598 {
599 int i;
600 Eterm res;
601 Uint need;
602 Eterm* hp;
603 HashBucket **bucket;
604 ErtsProcLocks proc_locks = ERTS_PROC_LOCK_MAIN;
605
606 ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(BIF_P);
607 reg_safe_read_lock(BIF_P, &proc_locks);
608 if (!proc_locks)
609 erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
610
611 bucket = process_reg.bucket;
612
613 /* work out how much heap we need & maybe garb, by scanning through
614 the registered process table */
615 need = 0;
616 for (i = 0; i < process_reg.size; i++) {
617 HashBucket *b = bucket[i];
618 while (b != NULL) {
619 need += 2;
620 b = b->next;
621 }
622 }
623
624 if (need == 0) {
625 reg_read_unlock();
626 BIF_RET(NIL);
627 }
628
629 hp = HAlloc(BIF_P, need);
630
631 /* scan through again and make the list */
632 res = NIL;
633
634 for (i = 0; i < process_reg.size; i++) {
635 HashBucket *b = bucket[i];
636 while (b != NULL) {
637 RegProc *reg = (RegProc *) b;
638
639 res = CONS(hp, reg->name, res);
640 hp += 2;
641 b = b->next;
642 }
643 }
644
645 reg_read_unlock();
646
647 BIF_RET(res);
648 }
649