/
process.ex
971 lines (717 loc) · 27.8 KB
/
process.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
defmodule Process do
@moduledoc """
Conveniences for working with processes and the process dictionary.
Besides the functions available in this module, the `Kernel` module
exposes and auto-imports some basic functionality related to processes
available through the following functions:
* `Kernel.spawn/1` and `Kernel.spawn/3`
* `Kernel.spawn_link/1` and `Kernel.spawn_link/3`
* `Kernel.spawn_monitor/1` and `Kernel.spawn_monitor/3`
* `Kernel.self/0`
* `Kernel.send/2`
While this module provides low-level conveniences to work with processes,
developers typically use abstractions such as `Agent`, `GenServer`,
`Registry`, `Supervisor` and `Task` for building their systems and
resort to this module for gathering information, trapping exits, links
and monitoring.
## Aliases
Aliases are a feature introduced in Erlang/OTP 24. An alias is a way
to refer to a PID in order to send messages to it. The advantage of using
aliases is that they can be deactivated even if the aliased process is still
running. If you send a message to a deactivated alias, nothing will happen.
This makes request/response scenarios easier to implement.
You can use `alias/0` or `alias/1` to set an alias, and then you can send
messages to that alias like you do with PIDs using `send/2`. To deactivate
an alias, you can use `unalias/1`. If you send a message to a deactivated alias,
nothing will happen.
For example, you could have a process that listens for `:ping` messages:
def server do
receive do
{:ping, source_alias} ->
send(source_alias, :pong)
server()
end
end
Now, another process might ping this process:
server = spawn(&server/0)
source_alias = Process.alias()
send(server, {:ping, source_alias})
receive do
:pong -> :pong
end
#=> :pong
If now you deactivate the `source_alias` and ping the server again, you
won't receive any response since the server will `send/2` the `:pong` response
to a deactivated alias.
Process.unalias(source_alias)
send(server, {:ping, source_alias})
receive do
:pong -> :pong
after
1000 -> :timeout
end
#=> :timeout
See also the [Process Aliases
section](https://www.erlang.org/doc/reference_manual/processes.html#process-aliases)
of the *Erlang reference manual*.
"""
@typedoc """
A process destination.
A remote or local PID, a local port, a locally registered name, or a tuple in
the form of `{registered_name, node}` for a registered name at another node.
"""
@type dest :: pid | port | (registered_name :: atom) | {registered_name :: atom, node}
@doc """
Tells whether the given process is alive on the local node.
If the process identified by `pid` is alive (that is, it's not exiting and has
not exited yet) than this function returns `true`. Otherwise, it returns
`false`.
`pid` must refer to a process running on the local node or `ArgumentError` is raised.
Inlined by the compiler.
"""
@spec alive?(pid) :: boolean
defdelegate alive?(pid), to: :erlang, as: :is_process_alive
@doc """
Returns all key-value pairs in the process dictionary.
Inlined by the compiler.
"""
@spec get() :: [{term, term}]
defdelegate get(), to: :erlang
@doc """
Returns the value for the given `key` in the process dictionary,
or `default` if `key` is not set.
## Examples
# Assuming :locale was not set
iex> Process.get(:locale, "pt")
"pt"
iex> Process.put(:locale, "fr")
nil
iex> Process.get(:locale, "pt")
"fr"
"""
@spec get(term, default :: term) :: term
def get(key, default \\ nil) do
case :erlang.get(key) do
:undefined -> default
value -> value
end
end
@doc """
Returns all keys in the process dictionary.
Inlined by the compiler.
## Examples
# Assuming :locale was not set
iex> :locale in Process.get_keys()
false
iex> Process.put(:locale, "pt")
nil
iex> :locale in Process.get_keys()
true
"""
@spec get_keys() :: [term]
defdelegate get_keys(), to: :erlang
@doc """
Returns all keys in the process dictionary that have the given `value`.
Inlined by the compiler.
"""
@spec get_keys(term) :: [term]
defdelegate get_keys(value), to: :erlang
@doc """
Stores the given `key`-`value` pair in the process dictionary.
The return value of this function is the value that was previously stored
under `key`, or `nil` in case no value was stored under it.
## Examples
# Assuming :locale was not set
iex> Process.put(:locale, "en")
nil
iex> Process.put(:locale, "fr")
"en"
"""
@spec put(term, term) :: term | nil
def put(key, value) do
nilify(:erlang.put(key, value))
end
@doc """
Deletes the given `key` from the process dictionary.
Returns the value that was under `key` in the process dictionary,
or `nil` if `key` was not stored in the process dictionary.
## Examples
iex> Process.put(:comments, ["comment", "other comment"])
iex> Process.delete(:comments)
["comment", "other comment"]
iex> Process.delete(:comments)
nil
"""
@spec delete(term) :: term | nil
def delete(key) do
nilify(:erlang.erase(key))
end
@doc """
Sends an exit signal with the given `reason` to `pid`.
The following behavior applies if `reason` is any term except `:normal`
or `:kill`:
1. If `pid` is not trapping exits, `pid` will exit with the given
`reason`.
2. If `pid` is trapping exits, the exit signal is transformed into a
message `{:EXIT, from, reason}` and delivered to the message queue
of `pid`.
If `reason` is the atom `:normal`, `pid` will not exit (unless `pid` is
the calling process, in which case it will exit with the reason `:normal`).
If it is trapping exits, the exit signal is transformed into a message
`{:EXIT, from, :normal}` and delivered to its message queue.
If `reason` is the atom `:kill`, that is if `Process.exit(pid, :kill)` is called,
an untrappable exit signal is sent to `pid` which will unconditionally exit
with reason `:killed`.
Inlined by the compiler.
## Examples
Process.exit(pid, :kill)
#=> true
"""
@spec exit(pid, term) :: true
defdelegate exit(pid, reason), to: :erlang
@doc """
Sleeps the current process for the given `timeout`.
`timeout` is either the number of milliseconds to sleep as an
integer or the atom `:infinity`. When `:infinity` is given,
the current process will sleep forever, and not
consume or reply to messages.
**Use this function with extreme care**. For almost all situations
where you would use `sleep/1` in Elixir, there is likely a
more correct, faster and precise way of achieving the same with
message passing.
For example, if you are waiting for a process to perform some
action, it is better to communicate the progress of such action
with messages.
In other words, **do not**:
Task.start_link(fn ->
do_something()
...
end)
# Wait until work is done
Process.sleep(2000)
But **do**:
parent = self()
Task.start_link(fn ->
do_something()
send(parent, :work_is_done)
...
end)
receive do
:work_is_done -> :ok
after
# Optional timeout
30_000 -> :timeout
end
For cases like the one above, `Task.async/1` and `Task.await/2` are
preferred.
Similarly, if you are waiting for a process to terminate,
monitor that process instead of sleeping. **Do not**:
Task.start_link(fn ->
...
end)
# Wait until task terminates
Process.sleep(2000)
Instead **do**:
{:ok, pid} =
Task.start_link(fn ->
...
end)
ref = Process.monitor(pid)
receive do
{:DOWN, ^ref, _, _, _} -> :task_is_down
after
# Optional timeout
30_000 -> :timeout
end
"""
@spec sleep(timeout) :: :ok
def sleep(timeout)
when is_integer(timeout) and timeout >= 0
when timeout == :infinity do
receive after: (timeout -> :ok)
end
@doc """
Sends a message to the given `dest`.
`dest` may be a remote or local PID, a local port, a locally
registered name, or a tuple in the form of `{registered_name, node}` for a
registered name at another node.
Inlined by the compiler.
## Options
* `:noconnect` - when used, if sending the message would require an
auto-connection to another node the message is not sent and `:noconnect` is
returned.
* `:nosuspend` - when used, if sending the message would cause the sender to
be suspended the message is not sent and `:nosuspend` is returned.
Otherwise the message is sent and `:ok` is returned.
## Examples
iex> Process.send({:name, :node_that_does_not_exist}, :hi, [:noconnect])
:noconnect
"""
@spec send(dest, msg, [option]) :: :ok | :noconnect | :nosuspend
when dest: dest(),
msg: any,
option: :noconnect | :nosuspend
defdelegate send(dest, msg, options), to: :erlang
@doc """
Sends `msg` to `dest` after `time` milliseconds.
If `dest` is a PID, it must be the PID of a local process, dead or alive.
If `dest` is an atom, it must be the name of a registered process
which is looked up at the time of delivery. No error is produced if the name does
not refer to a process.
The message is not sent immediately. Therefore, `dest` can receive other messages
in-between even when `time` is `0`.
This function returns a timer reference, which can be read with `read_timer/1`
or canceled with `cancel_timer/1`.
The timer will be automatically canceled if the given `dest` is a PID
which is not alive or when the given PID exits. Note that timers will not be
automatically canceled when `dest` is an atom (as the atom resolution is done
on delivery).
Inlined by the compiler.
## Options
* `:abs` - (boolean) when `false`, `time` is treated as relative to the
current monotonic time. When `true`, `time` is the absolute value of the
Erlang monotonic time at which `msg` should be delivered to `dest`.
To read more about Erlang monotonic time and other time-related concepts,
look at the documentation for the `System` module. Defaults to `false`.
## Examples
timer_ref = Process.send_after(pid, :hi, 1000)
"""
@spec send_after(pid | atom, term, non_neg_integer, [option]) :: reference
when option: {:abs, boolean}
def send_after(dest, msg, time, opts \\ []) do
:erlang.send_after(time, dest, msg, opts)
end
@doc """
Cancels a timer returned by `send_after/3`.
When the result is an integer, it represents the time in milliseconds
left until the timer would have expired.
When the result is `false`, a timer corresponding to `timer_ref` could not be
found. This can happen either because the timer expired, because it has
already been canceled, or because `timer_ref` never corresponded to a timer.
Even if the timer had expired and the message was sent, this function does not
tell you if the timeout message has arrived at its destination yet.
Inlined by the compiler.
## Options
* `:async` - (boolean) when `false`, the request for cancellation is
synchronous. When `true`, the request for cancellation is asynchronous,
meaning that the request to cancel the timer is issued and `:ok` is
returned right away. Defaults to `false`.
* `:info` - (boolean) whether to return information about the timer being
cancelled. When the `:async` option is `false` and `:info` is `true`, then
either an integer or `false` (like described above) is returned. If
`:async` is `false` and `:info` is `false`, `:ok` is returned. If `:async`
is `true` and `:info` is `true`, a message in the form `{:cancel_timer,
timer_ref, result}` (where `result` is an integer or `false` like
described above) is sent to the caller of this function when the
cancellation has been performed. If `:async` is `true` and `:info` is
`false`, no message is sent. Defaults to `true`.
"""
@spec cancel_timer(reference, options) :: non_neg_integer | false | :ok
when options: [async: boolean, info: boolean]
defdelegate cancel_timer(timer_ref, options \\ []), to: :erlang
@doc """
Reads a timer created by `send_after/3`.
When the result is an integer, it represents the time in milliseconds
left until the timer will expire.
When the result is `false`, a timer corresponding to `timer_ref` could not be
found. This can be either because the timer expired, because it has already
been canceled, or because `timer_ref` never corresponded to a timer.
Even if the timer had expired and the message was sent, this function does not
tell you if the timeout message has arrived at its destination yet.
Inlined by the compiler.
"""
@spec read_timer(reference) :: non_neg_integer | false
defdelegate read_timer(timer_ref), to: :erlang
@type spawn_opt ::
:link
| :monitor
| {:monitor, :erlang.monitor_option()}
| {:priority, :low | :normal | :high}
| {:fullsweep_after, non_neg_integer}
| {:min_heap_size, non_neg_integer}
| {:min_bin_vheap_size, non_neg_integer}
| {:max_heap_size, heap_size}
| {:message_queue_data, :off_heap | :on_heap}
@type spawn_opts :: [spawn_opt]
@doc """
Spawns the given function according to the given options.
The result depends on the given options. In particular,
if `:monitor` is given as an option, it will return a tuple
containing the PID and the monitoring reference, otherwise
just the spawned process PID.
More options are available; for the comprehensive list of available options
check `:erlang.spawn_opt/4`.
Inlined by the compiler.
## Examples
Process.spawn(fn -> 1 + 2 end, [:monitor])
#=> {#PID<0.93.0>, #Reference<0.18808174.1939079169.202418>}
Process.spawn(fn -> 1 + 2 end, [:link])
#=> #PID<0.95.0>
"""
@spec spawn((-> any), spawn_opts) :: pid | {pid, reference}
defdelegate spawn(fun, opts), to: :erlang, as: :spawn_opt
@doc """
Spawns the given function `fun` from module `mod`, passing the given `args`
according to the given options.
The result depends on the given options. In particular,
if `:monitor` is given as an option, it will return a tuple
containing the PID and the monitoring reference, otherwise
just the spawned process PID.
It also accepts extra options, for the list of available options
check `:erlang.spawn_opt/4`.
Inlined by the compiler.
"""
@spec spawn(module, atom, list, spawn_opts) :: pid | {pid, reference}
defdelegate spawn(mod, fun, args, opts), to: :erlang, as: :spawn_opt
@doc """
Starts monitoring the given `item` from the calling process.
Once the monitored process dies, a message is delivered to the
monitoring process in the shape of:
{:DOWN, ref, :process, object, reason}
where:
* `ref` is a monitor reference returned by this function;
* `object` is either a `pid` of the monitored process (if monitoring
a PID) or `{name, node}` (if monitoring a remote or local name);
* `reason` is the exit reason.
If the process is already dead when calling `Process.monitor/1`, a
`:DOWN` message is delivered immediately.
See ["The need for monitoring"](genservers.md#the-need-for-monitoring)
for an example. See `:erlang.monitor/2` for more information.
Inlined by the compiler.
## Examples
pid = spawn(fn -> 1 + 2 end)
#=> #PID<0.118.0>
Process.monitor(pid)
#=> #Reference<0.906660723.3006791681.40191>
Process.exit(pid, :kill)
#=> true
receive do
msg -> msg
end
#=> {:DOWN, #Reference<0.906660723.3006791681.40191>, :process, #PID<0.118.0>, :noproc}
"""
@spec monitor(pid | {name, node} | name) :: reference when name: atom
def monitor(item) do
:erlang.monitor(:process, item)
end
@doc """
Starts monitoring the given `item` from the calling process.
This function is similar to `monitor/1`, but accepts options to customize how
`item` is monitored. See `:erlang.monitor/3` for documentation on those
options.
Inlined by the compiler.
## Examples
pid =
spawn(fn ->
receive do
{:ping, source_alias} -> send(source_alias, :pong)
end
end)
#=> #PID<0.118.0>
ref_and_alias = Process.monitor(pid, alias: :reply_demonitor)
#=> #Reference<0.906660723.3006791681.40191>
send(pid, {:ping, ref_and_alias})
receive do: msg -> msg
#=> :pong
receive do: msg -> msg
#=> {:DOWN, #Reference<0.906660723.3006791681.40191>, :process, #PID<0.118.0>, :noproc}
"""
@doc since: "1.15.0"
@spec monitor(pid | {name, node} | name, [:erlang.monitor_option()]) :: reference
when name: atom
def monitor(item, options) do
:erlang.monitor(:process, item, options)
end
@doc """
Demonitors the monitor identified by the given `reference`.
If `monitor_ref` is a reference which the calling process
obtained by calling `monitor/1`, that monitoring is turned off.
If the monitoring is already turned off, nothing happens.
See `:erlang.demonitor/2` for more information.
Inlined by the compiler.
## Examples
pid = spawn(fn -> 1 + 2 end)
ref = Process.monitor(pid)
Process.demonitor(ref)
#=> true
"""
@spec demonitor(reference, options :: [:flush | :info]) :: boolean
defdelegate demonitor(monitor_ref, options \\ []), to: :erlang
@doc """
Returns a list of PIDs corresponding to all the
processes currently existing on the local node.
Note that if a process is exiting, it is considered to exist but not be
alive. This means that for such process, `alive?/1` will return `false` but
its PID will be part of the list of PIDs returned by this function.
See `:erlang.processes/0` for more information.
Inlined by the compiler.
## Examples
Process.list()
#=> [#PID<0.0.0>, #PID<0.1.0>, #PID<0.2.0>, #PID<0.3.0>, ...]
"""
@spec list() :: [pid]
defdelegate list(), to: :erlang, as: :processes
@doc """
Creates a link between the calling process and the given item (process or
port).
Links are bidirectional. Linked processes can be unlinked by using `unlink/1`.
If such a link exists already, this function does nothing since there can only
be one link between two given processes. If a process tries to create a link
to itself, nothing will happen.
When two processes are linked, each one receives exit signals from the other
(see also `exit/2`). Let's assume `pid1` and `pid2` are linked. If `pid2`
exits with a reason other than `:normal` (which is also the exit reason used
when a process finishes its job) and `pid1` is not trapping exits (see
`flag/2`), then `pid1` will exit with the same reason as `pid2` and in turn
emit an exit signal to all its other linked processes. The behavior when
`pid1` is trapping exits is described in `exit/2`.
See `:erlang.link/1` for more information.
Inlined by the compiler.
"""
@spec link(pid | port) :: true
defdelegate link(pid_or_port), to: :erlang
@doc """
Removes the link between the calling process and the given item (process or
port).
If there is no such link, this function does nothing. If `pid_or_port` does
not exist, this function does not produce any errors and simply does nothing.
The return value of this function is always `true`.
See `:erlang.unlink/1` for more information.
Inlined by the compiler.
"""
@spec unlink(pid | port) :: true
defdelegate unlink(pid_or_port), to: :erlang
@doc """
Registers the given `pid_or_port` under the given `name` on the local node.
`name` must be an atom and can then be used instead of the
PID/port identifier when sending messages with `Kernel.send/2`.
`register/2` will fail with `ArgumentError` in any of the following cases:
* the PID/Port is not existing locally and alive
* the name is already registered
* the `pid_or_port` is already registered under a different `name`
The following names are reserved and cannot be assigned to
processes nor ports:
* `nil`
* `false`
* `true`
* `:undefined`
## Examples
Process.register(self(), :test)
#=> true
send(:test, :hello)
#=> :hello
send(:wrong_name, :hello)
** (ArgumentError) argument error
"""
@spec register(pid | port, atom) :: true
def register(pid_or_port, name)
when is_atom(name) and name not in [nil, false, true, :undefined] do
:erlang.register(name, pid_or_port)
catch
:error, :badarg when node(pid_or_port) != node() ->
message = "could not register #{inspect(pid_or_port)} because it belongs to another node"
:erlang.error(ArgumentError.exception(message), [pid_or_port, name])
:error, :badarg ->
message =
"could not register #{inspect(pid_or_port)} with " <>
"name #{inspect(name)} because it is not alive, the name is already " <>
"taken, or it has already been given another name"
:erlang.error(ArgumentError.exception(message), [pid_or_port, name])
end
@doc """
Removes the registered `name`, associated with a PID
or a port identifier.
Fails with `ArgumentError` if the name is not registered
to any PID or port.
Inlined by the compiler.
## Examples
Process.register(self(), :test)
#=> true
Process.unregister(:test)
#=> true
Process.unregister(:wrong_name)
** (ArgumentError) argument error
"""
@spec unregister(atom) :: true
defdelegate unregister(name), to: :erlang
@doc """
Returns the PID or port identifier registered under `name` or `nil` if the
name is not registered.
See `:erlang.whereis/1` for more information.
## Examples
Process.register(self(), :test)
Process.whereis(:test)
#=> #PID<0.84.0>
Process.whereis(:wrong_name)
#=> nil
"""
@spec whereis(atom) :: pid | port | nil
def whereis(name) do
nilify(:erlang.whereis(name))
end
@doc """
Returns the PID of the group leader for the calling process.
Inlined by the compiler.
## Examples
Process.group_leader()
#=> #PID<0.53.0>
"""
@spec group_leader() :: pid
defdelegate group_leader(), to: :erlang
@doc """
Sets the group leader of the given `pid` to `leader`.
Typically, this is used when a process started from a certain shell should
have a group leader other than `:init`.
Inlined by the compiler.
"""
@spec group_leader(pid, leader :: pid) :: true
def group_leader(pid, leader) do
:erlang.group_leader(leader, pid)
end
@doc """
Returns a list of names which have been registered using `register/2`.
Inlined by the compiler.
## Examples
Process.register(self(), :test)
Process.registered()
#=> [:test, :elixir_config, :inet_db, ...]
"""
@spec registered() :: [atom]
defdelegate registered(), to: :erlang
@typep heap_size ::
non_neg_integer
| %{size: non_neg_integer, kill: boolean, error_logger: boolean}
@typep priority_level :: :low | :normal | :high | :max
@doc """
Sets the given `flag` to `value` for the calling process.
Returns the old value of `flag`.
See `:erlang.process_flag/2` for more information.
Inlined by the compiler.
"""
@spec flag(:error_handler, module) :: module
@spec flag(:max_heap_size, heap_size) :: heap_size
# :off_heap | :on_heap twice because :erlang.message_queue_data() is not exported
@spec flag(:message_queue_data, :off_heap | :on_heap) :: :off_heap | :on_heap
@spec flag(:min_bin_vheap_size, non_neg_integer) :: non_neg_integer
@spec flag(:min_heap_size, non_neg_integer) :: non_neg_integer
@spec flag(:priority, priority_level) :: priority_level
@spec flag(:save_calls, 0..10000) :: 0..10000
@spec flag(:sensitive, boolean) :: boolean
@spec flag(:trap_exit, boolean) :: boolean
defdelegate flag(flag, value), to: :erlang, as: :process_flag
@doc """
Sets the given `flag` to `value` for the given process `pid`.
Returns the old value of `flag`.
It raises `ArgumentError` if `pid` is not a local process.
The allowed values for `flag` are only a subset of those allowed in `flag/2`,
namely `:save_calls`.
See `:erlang.process_flag/3` for more information.
Inlined by the compiler.
"""
@spec flag(pid, :save_calls, 0..10000) :: 0..10000
defdelegate flag(pid, flag, value), to: :erlang, as: :process_flag
@doc """
Returns information about the process identified by `pid`, or returns `nil` if the process
is not alive.
Use this only for debugging information.
See `:erlang.process_info/1` for more information.
"""
@spec info(pid) :: keyword | nil
def info(pid) do
nilify(:erlang.process_info(pid))
end
@doc """
Returns information about the process identified by `pid`,
or returns `nil` if the process is not alive.
See `:erlang.process_info/2` for more information.
"""
@spec info(pid, atom | [atom]) :: {atom, term} | [{atom, term}] | nil
def info(pid, spec)
def info(pid, :registered_name) do
case :erlang.process_info(pid, :registered_name) do
:undefined -> nil
[] -> {:registered_name, []}
other -> other
end
end
def info(pid, spec) when is_atom(spec) or is_list(spec) do
nilify(:erlang.process_info(pid, spec))
end
@doc """
Puts the calling process into a "hibernation" state.
The calling process is put into a waiting state
where its memory allocation has been reduced as much as possible,
which is useful if the process does not expect to receive any messages
in the near future.
See `:erlang.hibernate/3` for more information.
Inlined by the compiler.
"""
@spec hibernate(module, atom, list) :: no_return
defdelegate hibernate(mod, fun_name, args), to: :erlang
@type alias_opt :: :explicit_unalias | :reply
@typedoc """
An alias returned by `alias/0` or `alias/1`.
See [the module documentation](#module-aliases) for more information about aliases.
"""
@type alias :: reference
@doc """
Creates a process alias.
This is the same as calling `alias/1` as `alias([:explicit_unalias])`. See
also `:erlang.alias/0`.
Inlined by the compiler.
## Examples
alias = Process.alias()
"""
@doc since: "1.15.0"
@spec alias() :: alias
defdelegate alias(), to: :erlang
@doc """
Creates a process alias.
See [the module documentation](#module-aliases) for more information about aliases.
See also `:erlang.alias/1`.
Inlined by the compiler.
## Examples
alias = Process.alias([:reply])
"""
@doc since: "1.15.0"
@spec alias([alias_opt]) :: alias
defdelegate alias(options), to: :erlang
@doc """
Explicitly deactivates a process alias.
Returns `true` if `alias` was a currently-active alias for current processes,
or `false` otherwise.
See [the module documentation](#module-aliases) for more information about aliases.
See also `:erlang.unalias/1`.
Inlined by the compiler.
## Examples
alias = Process.alias()
Process.unalias(alias)
#=> true
"""
@doc since: "1.15.0"
@spec unalias(alias) :: boolean
defdelegate unalias(alias), to: :erlang
@doc """
Add a descriptive term to the current process.
The term does not need to be unique, and in Erlang/OTP 27+ will be shown in
Observer and in crash logs.
This label may be useful for identifying a process as one of multiple in a
given role, such as `:queue_worker` or `{:live_chat, user_id}`.
## Examples
Process.set_label(:worker)
#=> :ok
Process.set_label({:any, "term"})
#=> :ok
"""
@doc since: "1.17.0"
@spec set_label(term()) :: :ok
def set_label(label) do
# TODO: switch to `:proc_lib.set_label/2` when we require Erlang/OTP 27+
Process.put(:"$process_label", label)
# mimic return value of `:proc_lib.set_label/2`
:ok
end
@compile {:inline, nilify: 1}
defp nilify(:undefined), do: nil
defp nilify(other), do: other
end