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 * Assumes that main lock is locked (and only the main lock) on c_p.
166 *
167 * On return, c_p->value is set to one of the following values:
168 *
169 * am_registered_name The process or port already has a name.
170 * am_notalive The process is no longer alive.
171 * am_none Success or other error reason.
172 */
erts_register_name(Process * c_p,Eterm name,Eterm id)173 int erts_register_name(Process *c_p, Eterm name, Eterm id)
174 {
175 int res = 0;
176 Process *proc = NULL;
177 Port *port = NULL;
178 RegProc r, *rp;
179 ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(c_p);
180
181 c_p->fvalue = am_none;
182 if (is_not_atom(name) || name == am_undefined)
183 return res;
184
185 if (c_p->common.id == id) /* A very common case I think... */
186 proc = c_p;
187 else {
188 if (is_not_internal_pid(id) && is_not_internal_port(id))
189 return res;
190 erts_proc_unlock(c_p, ERTS_PROC_LOCK_MAIN);
191 if (is_internal_port(id)) {
192 port = erts_id2port(id);
193 if (!port)
194 goto done;
195 }
196 }
197
198 {
199 ErtsProcLocks proc_locks = proc ? ERTS_PROC_LOCK_MAIN : 0;
200 reg_safe_write_lock(proc, &proc_locks);
201
202 if (proc && !proc_locks)
203 erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
204 }
205
206 if (is_internal_pid(id)) {
207 if (!proc)
208 proc = erts_pid2proc(NULL, 0, id, ERTS_PROC_LOCK_MAIN);
209 r.p = proc;
210 if (!proc) {
211 c_p->fvalue = am_notalive;
212 goto done;
213 }
214 if (proc->common.u.alive.reg) {
215 c_p->fvalue = am_registered_name;
216 goto done;
217 }
218 r.pt = NULL;
219 }
220 else {
221 ASSERT(!INVALID_PORT(port, id));
222 ERTS_LC_ASSERT(erts_lc_is_port_locked(port));
223 r.pt = port;
224 if (r.pt->common.u.alive.reg) {
225 c_p->fvalue = am_registered_name;
226 goto done;
227 }
228 r.p = NULL;
229 }
230
231 r.name = name;
232
233 rp = (RegProc*) hash_put(&process_reg, (void*) &r);
234 if (proc && rp->p == proc) {
235 if (IS_TRACED_FL(proc, F_TRACE_PROCS)) {
236 trace_proc(proc, ERTS_PROC_LOCK_MAIN,
237 proc, am_register, name);
238 }
239 proc->common.u.alive.reg = rp;
240 }
241 else if (port && rp->pt == port) {
242 if (IS_TRACED_FL(port, F_TRACE_PORTS)) {
243 trace_port(port, am_register, name);
244 }
245 port->common.u.alive.reg = rp;
246 }
247
248 if ((rp->p && rp->p->common.id == id)
249 || (rp->pt && rp->pt->common.id == id)) {
250 res = 1;
251 }
252
253 done:
254 reg_write_unlock();
255 if (port)
256 erts_port_release(port);
257 if (c_p != proc) {
258 if (proc)
259 erts_proc_unlock(proc, ERTS_PROC_LOCK_MAIN);
260 erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
261 }
262 return res;
263 }
264
265 /*
266 *
267 * When smp support is enabled:
268 * * Assumes that main lock is locked (and only main lock)
269 * on c_p.
270 *
271 * * am_undefined is returned if c_p became exiting.
272 */
273
274 Eterm
erts_whereis_name_to_id(Process * c_p,Eterm name)275 erts_whereis_name_to_id(Process *c_p, Eterm name)
276 {
277 Eterm res = am_undefined;
278 ErtsProcLocks c_p_locks = 0;
279 RegProc *rp, tmpl;
280 if (c_p) {
281 c_p_locks = ERTS_PROC_LOCK_MAIN;
282 ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(c_p);
283 }
284 reg_safe_read_lock(c_p, &c_p_locks);
285
286 if (c_p && !c_p_locks)
287 erts_proc_lock(c_p, ERTS_PROC_LOCK_MAIN);
288
289 tmpl.name = name;
290 rp = hash_fetch(&process_reg, &tmpl, (H_FUN)reg_hash, (HCMP_FUN)reg_cmp);
291
292 if (rp) {
293 if (rp->p)
294 res = rp->p->common.id;
295 else if (rp->pt)
296 res = rp->pt->common.id;
297 }
298
299 reg_read_unlock();
300
301 ASSERT(is_internal_pid(res) || is_internal_port(res) || res==am_undefined);
302
303 return res;
304 }
305
306
307 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)308 erts_whereis_name(Process *c_p,
309 ErtsProcLocks c_p_locks,
310 Eterm name,
311 Process** proc,
312 ErtsProcLocks need_locks,
313 int flags,
314 Port** port,
315 int lock_port)
316 {
317 RegProc* rp = NULL, tmpl;
318 ErtsProcLocks current_c_p_locks;
319 Port *pending_port = NULL;
320
321 if (!c_p)
322 c_p_locks = 0;
323 current_c_p_locks = c_p_locks;
324
325 restart:
326
327 reg_safe_read_lock(c_p, ¤t_c_p_locks);
328
329 /* Locked locks:
330 * - port lock on pending_port if pending_port != NULL
331 * - read reg lock
332 * - current_c_p_locks (either c_p_locks or 0) on c_p
333 */
334
335 tmpl.name = name;
336 rp = hash_fetch(&process_reg, &tmpl, (H_FUN)reg_hash, (HCMP_FUN)reg_cmp);
337
338 if (proc) {
339 if (!rp)
340 *proc = NULL;
341 else {
342 if (!rp->p)
343 *proc = NULL;
344 else {
345 if (need_locks) {
346 erts_proc_safelock(c_p,
347 current_c_p_locks,
348 c_p_locks,
349 rp->p,
350 0,
351 need_locks);
352 current_c_p_locks = c_p_locks;
353 }
354 if ((flags & ERTS_P2P_FLG_ALLOW_OTHER_X) || is_proc_alive(rp->p))
355 *proc = rp->p;
356 else {
357 if (need_locks)
358 erts_proc_unlock(rp->p, need_locks);
359 *proc = NULL;
360 }
361 }
362 if (*proc && (flags & ERTS_P2P_FLG_INC_REFC))
363 erts_proc_inc_refc(*proc);
364 }
365 }
366
367 if (port) {
368 if (!rp || !rp->pt)
369 *port = NULL;
370 else {
371 if (lock_port) {
372 if (pending_port == rp->pt)
373 pending_port = NULL;
374 else {
375 if (pending_port) {
376 /* Ahh! Registered port changed while reg lock
377 was unlocked... */
378 erts_port_release(pending_port);
379 pending_port = NULL;
380 }
381
382 if (erts_port_trylock(rp->pt) == EBUSY) {
383 Eterm id = rp->pt->common.id; /* id read only... */
384 /* Unlock all locks, acquire port lock, and restart... */
385 if (current_c_p_locks) {
386 erts_proc_unlock(c_p, current_c_p_locks);
387 current_c_p_locks = 0;
388 }
389 reg_read_unlock();
390 pending_port = erts_id2port(id);
391 goto restart;
392 }
393 }
394 ERTS_LC_ASSERT(erts_lc_is_port_locked(rp->pt));
395 }
396 *port = rp->pt;
397 }
398 }
399
400 if (c_p && !current_c_p_locks)
401 erts_proc_lock(c_p, c_p_locks);
402 if (pending_port)
403 erts_port_release(pending_port);
404
405 reg_read_unlock();
406 }
407
408 Process *
erts_whereis_process(Process * c_p,ErtsProcLocks c_p_locks,Eterm name,ErtsProcLocks need_locks,int flags)409 erts_whereis_process(Process *c_p,
410 ErtsProcLocks c_p_locks,
411 Eterm name,
412 ErtsProcLocks need_locks,
413 int flags)
414 {
415 Process *proc;
416 erts_whereis_name(c_p, c_p_locks, name, &proc, need_locks, flags, NULL, 0);
417 return proc;
418 }
419
420
421 /*
422 * Unregister a name
423 * Return 0 if not registered
424 * Otherwise returns 1
425 *
426 */
erts_unregister_name(Process * c_p,ErtsProcLocks c_p_locks,Port * c_prt,Eterm name)427 int erts_unregister_name(Process *c_p,
428 ErtsProcLocks c_p_locks,
429 Port *c_prt,
430 Eterm name)
431 {
432 int res = 0;
433 RegProc r, *rp;
434 Port *port = c_prt;
435 ErtsProcLocks current_c_p_locks = 0;
436
437 /*
438 * SMP note: If 'c_prt != NULL' and 'c_prt->reg->name == name',
439 * we are *not* allowed to temporarily release the lock
440 * on c_prt.
441 */
442
443 if (!c_p) {
444 c_p_locks = 0;
445 }
446 current_c_p_locks = c_p_locks;
447
448 restart:
449
450 reg_safe_write_lock(c_p, ¤t_c_p_locks);
451
452 r.name = name;
453 if (is_non_value(name)) {
454 /* Unregister current process name */
455 ASSERT(c_p);
456 if (current_c_p_locks != c_p_locks) {
457 erts_proc_lock(c_p, c_p_locks);
458 current_c_p_locks = c_p_locks;
459 }
460 if (c_p->common.u.alive.reg) {
461 r.name = c_p->common.u.alive.reg->name;
462 } else {
463 /* Name got unregistered while main lock was released */
464 res = 0;
465 goto done;
466 }
467 }
468
469 if ((rp = (RegProc*) hash_get(&process_reg, (void*) &r)) != NULL) {
470 if (rp->pt) {
471 if (port != rp->pt) {
472 if (port) {
473 ASSERT(port != c_prt);
474 erts_port_release(port);
475 port = NULL;
476 }
477
478 if (erts_port_trylock(rp->pt) == EBUSY) {
479 Eterm id = rp->pt->common.id; /* id read only... */
480 /* Unlock all locks, acquire port lock, and restart... */
481 if (current_c_p_locks) {
482 erts_proc_unlock(c_p, current_c_p_locks);
483 current_c_p_locks = 0;
484 }
485 reg_write_unlock();
486 port = erts_id2port(id);
487 goto restart;
488 }
489 port = rp->pt;
490 }
491
492 ASSERT(rp->pt == port);
493 ERTS_LC_ASSERT(erts_lc_is_port_locked(port));
494
495 rp->pt->common.u.alive.reg = NULL;
496
497 if (IS_TRACED_FL(port, F_TRACE_PORTS)) {
498 if (current_c_p_locks) {
499 erts_proc_unlock(c_p, current_c_p_locks);
500 current_c_p_locks = 0;
501 }
502 trace_port(port, am_unregister, r.name);
503 }
504
505 } else if (rp->p) {
506
507 erts_proc_safelock(c_p,
508 current_c_p_locks,
509 c_p_locks,
510 rp->p,
511 (c_p == rp->p) ? current_c_p_locks : 0,
512 ERTS_PROC_LOCK_MAIN);
513 current_c_p_locks = c_p_locks;
514 rp->p->common.u.alive.reg = NULL;
515 if (IS_TRACED_FL(rp->p, F_TRACE_PROCS)) {
516 trace_proc(rp->p, (c_p == rp->p) ? c_p_locks : ERTS_PROC_LOCK_MAIN,
517 rp->p, am_unregister, r.name);
518 }
519 if (rp->p != c_p) {
520 erts_proc_unlock(rp->p, ERTS_PROC_LOCK_MAIN);
521 }
522 }
523 hash_erase(&process_reg, (void*) &r);
524 res = 1;
525 }
526
527 done:
528
529 reg_write_unlock();
530 if (c_prt != port) {
531 if (port) {
532 erts_port_release(port);
533 }
534 if (c_prt) {
535 erts_port_lock(c_prt);
536 }
537 }
538 if (c_p && !current_c_p_locks) {
539 erts_proc_lock(c_p, c_p_locks);
540 }
541 return res;
542 }
543
process_reg_sz(void)544 int process_reg_sz(void)
545 {
546 int sz;
547 int lock = !ERTS_IS_CRASH_DUMPING;
548 if (lock)
549 reg_read_lock();
550 sz = hash_table_sz(&process_reg);
551 if (lock)
552 reg_read_unlock();
553 return sz;
554 }
555
556 /**********************************************************************/
557
558 #include "bif.h"
559
560 struct registered_foreach_arg {
561 Eterm res;
562 Eterm *hp;
563 };
564
565 static void
registered_foreach(RegProc * reg,struct registered_foreach_arg * arg)566 registered_foreach(RegProc *reg, struct registered_foreach_arg *arg)
567 {
568 arg->res = CONS(arg->hp, reg->name, arg->res);
569 arg->hp += 2;
570 }
571
572 /* return a list of the registered processes */
573
registered_0(BIF_ALIST_0)574 BIF_RETTYPE registered_0(BIF_ALIST_0)
575 {
576 struct registered_foreach_arg arg;
577 Uint need;
578 ErtsProcLocks proc_locks = ERTS_PROC_LOCK_MAIN;
579
580 ERTS_CHK_HAVE_ONLY_MAIN_PROC_LOCK(BIF_P);
581 reg_safe_read_lock(BIF_P, &proc_locks);
582 if (!proc_locks)
583 erts_proc_lock(BIF_P, ERTS_PROC_LOCK_MAIN);
584
585 /* work out how much heap we need */
586 need = process_reg.nobjs * 2;
587
588 if (need == 0) {
589 reg_read_unlock();
590 BIF_RET(NIL);
591 }
592
593 /* scan through again and make the list */
594 arg.hp = HAlloc(BIF_P, need);
595 arg.res = NIL;
596
597 hash_foreach(&process_reg, (HFOREACH_FUN)registered_foreach, &arg);
598
599 reg_read_unlock();
600
601 BIF_RET(arg.res);
602 }
603