aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator/beam/erl_proc_sig_queue.h
blob: 2b055e73bc8fa3dd9520b81a0dc5b5ece29f488d (plain) (blame)
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
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
/*
 * %CopyrightBegin%
 * 
 * Copyright Ericsson AB 2018. All Rights Reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 * %CopyrightEnd%
 */

/*
 * Description:	Process signal queue implementation.
 *
 *              Currently the following signals are handled:
 *              - Messages
 *              - Exit
 *              - Monitor
 *              - Demonitor
 *              - Monitor down
 *              - Persistent monitor message
 *              - Link
 *              - Unlink
 *              - Group leader
 *              - Is process alive
 *              - Process info request
 *              - Suspend request (monitor of suspend type)
 *              - Resume request (demonitor of suspend type)
 *              - Suspend cleanup (monitor down of suspend type)
 *              - Sync suspend
 *              - RPC request
 *              - Trace change
 *
 *              The signal queue consists of three parts:
 *              - Outer queue (sig_inq field in process struct)
 *              - Middle queue (sig_qs field in process struct)
 *              - Inner queue (sig_qs field in process struct)
 *
 *              Incoming signals are placed in the outer queue
 *              by other processes, ports, or by the runtime system
 *              itself. This queue is protected by the msgq process
 *              lock and may be accessed by any other entity. While
 *              a signal is located in the outer queue, it is still
 *              in transit between sender and receiver.
 *
 *              The middle and the inner queues are private to the
 *              receiving process and can only be accessed while
 *              holding the main process lock. The signal changes
 *              from being in transit to being received while in
 *              the middle queue. Non-message signals are handled
 *              immediately upon reception while message signals
 *              are moved into the inner queue.
 *
 *              In the outer and middle queues both message signals
 *              and non-message signals are mixed. Signals in these
 *              queues are referenced using two single linked lists.
 *              One single linked list that go through all signals
 *              in the queue and another single linked list that
 *              goes through only non-message signals. The list
 *              through the non-message signals is used for fast
 *              access to these signals in the middle queue, since
 *              these should be handled immediately upon reception.
 *
 *              The inner queue consists only of one single linked
 *              list through the message signals. A receive
 *              expression can only operate on messages once they
 *              have entered the inner queue.
 *
 * Author: 	Rickard Green
 */

#ifndef ERTS_PROC_SIG_QUEUE_H_TYPE__
#define ERTS_PROC_SIG_QUEUE_H_TYPE__

#if 0
#  define ERTS_PROC_SIG_HARD_DEBUG
#endif
#if 0
#  define ERTS_PROC_SIG_HARD_DEBUG_SIGQ_MSG_LEN
#endif

struct erl_mesg;
struct erl_dist_external;

typedef struct {
    struct erl_mesg *next;
    union {
        struct erl_mesg **next;
        void *attachment;
    } specific;
    Eterm tag;
} ErtsSignalCommon;

#define ERTS_SIG_HANDLE_REDS_MAX_PREFERED (CONTEXT_REDS/40)

#ifdef ERTS_PROC_SIG_HARD_DEBUG
#  define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(P)       \
    ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__((P), "")
#  define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(P, QL) \
    ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__((P), (QL), "")
#  define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__(P, What)   \
    erts_proc_sig_hdbg_check_in_queue((P), (What), __FILE__, __LINE__)
#  define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__(P, QL, What)              \
    erts_proc_sig_hdbg_check_priv_queue((P), (QL), (What), __FILE__, __LINE__)
struct process;
void erts_proc_sig_hdbg_check_priv_queue(struct process *c_p, int qlock,
                                         char *what, char *file, int line);
void erts_proc_sig_hdbg_check_in_queue(struct process *c_p, char *what,
                                       char *file, int line);
#else
#  define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(P)
#  define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(P, QL)
#  define ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE__(P, What)
#define ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE__(P, QL, What)
#endif

#endif

#if !defined(ERTS_PROC_SIG_QUEUE_H__) && !defined(ERTS_PROC_SIG_QUEUE_TYPE_ONLY)
#define ERTS_PROC_SIG_QUEUE_H__

#define ERTS_SIG_Q_OP_BITS      8                      
#define ERTS_SIG_Q_OP_SHIFT     0
#define ERTS_SIG_Q_OP_MASK      ((1 << ERTS_SIG_Q_OP_BITS) - 1)

#define ERTS_SIG_Q_TYPE_BITS    8
#define ERTS_SIG_Q_TYPE_SHIFT   ERTS_SIG_Q_OP_BITS
#define ERTS_SIG_Q_TYPE_MASK    ((1 << ERTS_SIG_Q_TYPE_BITS) - 1)

#define ERTS_SIG_Q_NON_X_BITS__ (_HEADER_ARITY_OFFS \
                                 + ERTS_SIG_Q_OP_BITS \
                                 + ERTS_SIG_Q_TYPE_BITS)

#define ERTS_SIG_Q_XTRA_BITS    (32 - ERTS_SIG_Q_NON_X_BITS__)
#define ERTS_SIG_Q_XTRA_SHIFT   (ERTS_SIG_Q_OP_BITS \
                                 + ERTS_SIG_Q_TYPE_BITS)
#define ERTS_SIG_Q_XTRA_MASK    ((1 << ERTS_SIG_Q_XTRA_BITS) - 1)


#define ERTS_PROC_SIG_OP(Tag) \
    ((int) (_unchecked_thing_arityval((Tag)) \
            >> ERTS_SIG_Q_OP_SHIFT) & ERTS_SIG_Q_OP_MASK)

#define ERTS_PROC_SIG_TYPE(Tag) \
    ((Uint16) (_unchecked_thing_arityval((Tag)) \
               >> ERTS_SIG_Q_TYPE_SHIFT) & ERTS_SIG_Q_TYPE_MASK)

#define ERTS_PROC_SIG_XTRA(Tag) \
    ((Uint32) (_unchecked_thing_arityval((Tag)) \
               >> ERTS_SIG_Q_XTRA_SHIFT) & ERTS_SIG_Q_XTRA_MASK)

#define ERTS_PROC_SIG_MAKE_TAG(Op, Type, Xtra)                  \
    (ASSERT(0 <= (Xtra) && (Xtra) <= ERTS_SIG_Q_XTRA_MASK),     \
     _make_header((((Type) & ERTS_SIG_Q_TYPE_MASK)              \
                   << ERTS_SIG_Q_TYPE_SHIFT)                    \
                  | (((Op) & ERTS_SIG_Q_OP_MASK)                \
                     << ERTS_SIG_Q_OP_SHIFT)                    \
                  | (((Xtra) & ERTS_SIG_Q_XTRA_MASK)            \
                     << ERTS_SIG_Q_XTRA_SHIFT),                 \
                  _TAG_HEADER_EXTERNAL_PID))


/*
 * ERTS_SIG_Q_OP_MSGQ_LEN_OFFS_MARK is not an actual
 * operation. We keep it at the top of the OP range,
 * larger than ERTS_SIG_Q_OP_MAX.
 */
#define ERTS_SIG_Q_OP_MSGQ_LEN_OFFS_MARK ERTS_SIG_Q_OP_MASK

#define ERTS_PROC_SIG_MSGQ_LEN_OFFS_MARK \
    ERTS_PROC_SIG_MAKE_TAG(ERTS_SIG_Q_OP_MSGQ_LEN_OFFS_MARK,0,0)

struct dist_entry_;

/*
 * Send operations of currently supported process signals follow...
 */

/**
 *
 * @brief Send an exit signal to a process.
 *
 *
 * @param[in]     c_p           Pointer to process struct of
 *                              currently executing process.
 *
 * @param[in]     from          Identifier of sender.
 *
 * @param[in]     to            Identifier of local process
 *                              to send signal to.
 *
 * @param[in]     reason        Exit reason.
 *
 * @param[in]     token         Seq trace token.
 *
 * @param[in]     normal_kills  If non-zero, also normal exit
 *                              reason will kill the receiver
 *                              if it is not trapping exit.
 *
 */
void
erts_proc_sig_send_exit(Process *c_p, Eterm from, Eterm to,
                        Eterm reason, Eterm token, int normal_kills);

/**
 *
 * @brief Send an exit signal to a process.
 *
 * This function is used instead of erts_proc_sig_send_link_exit()
 * when the signal arrives via the distribution and
 * therefore no link structure is available.
 *
 * @param[in]     dep           Distribution entry of channel
 *                              that the signal arrived on.
 *
 * @param[in]     from          Identifier of sender.
 *
 * @param[in]     to            Identifier of receiver.
 *
 * @param[in]     dist_ext      The exit reason in external term format
 *
 * @param[in]     hfrag         Heap frag with trace token and dist_ext
 *                              iff available, otherwise NULL.
 *
 * @param[in]     reason        Exit reason.
 *
 * @param[in]     token         Seq trace token.
 *
 */
void
erts_proc_sig_send_dist_exit(DistEntry *dep,
                             Eterm from, Eterm to,
                             ErtsDistExternal *dist_ext,
                             ErlHeapFragment *hfrag,
                             Eterm reason, Eterm token);

/**
 *
 * @brief Send an exit signal due to broken link to a process.
 *
 *
 * @param[in]     c_p           Pointer to process struct of
 *                              currently executing process.
 *
 * @param[in]     from          Identifier of sender.
 *
 * @param[in]     lnk           Pointer to link structure
 *                              from the sending side. It
 *                              should contain information
 *                              about receiver.
 *
 * @param[in]     reason        Exit reason.
 *
 * @param[in]     token         Seq trace token.
 *
 */
void
erts_proc_sig_send_link_exit(Process *c_p, Eterm from, ErtsLink *lnk,
                             Eterm reason, Eterm token);

/**
 *
 * @brief Send an link signal to a process.
 *
 *
 * @param[in]     c_p           Pointer to process struct of
 *                              currently executing process.
 *
 * @param[in]     to            Identifier of receiver.
 *
 * @param[in]     lnk           Pointer to link structure to
 *                              insert on receiver side.
 *
 * @return                      A non-zero value if
 *                              signal was successfully
 *                              sent. If a zero, value
 *                              the signal was not sent
 *                              due to the receiver not
 *                              existing. The sender
 *                              needs to deallocate the
 *                              link structure.
 *
 */
int
erts_proc_sig_send_link(Process *c_p, Eterm to, ErtsLink *lnk);

/**
 *
 * @brief Send an unlink signal to a process.
 *
 *
 * @param[in]     c_p           Pointer to process struct of
 *                              currently executing process.
 *
 * @param[in]     lnk           Pointer to link structure from
 *                              the sending side. It should
 *                              contain information about
 *                              receiver.
 */
void
erts_proc_sig_send_unlink(Process *c_p, ErtsLink *lnk);

/**
 *
 * @brief Send an exit signal due to broken link to a process.
 *
 * This function is used instead of erts_proc_sig_send_link_exit()
 * when the signal arrives via the distribution and
 * therefore no link structure is available.
 *
 * @param[in]     dep           Distribution entry of channel
 *                              that the signal arrived on.
 *
 * @param[in]     from          Identifier of sender.
 *
 * @param[in]     to            Identifier of receiver.
 *
 * @param[in]     dist_ext      The exit reason in external term format
 *
 * @param[in]     hfrag         Heap frag with trace token and dist_ext
 *                              iff available, otherwise NULL.
 *
 * @param[in]     reason        Exit reason.
 *
 * @param[in]     token         Seq trace token.
 *
 */
void
erts_proc_sig_send_dist_link_exit(struct dist_entry_ *dep,
                                  Eterm from, Eterm to,
                                  ErtsDistExternal *dist_ext,
                                  ErlHeapFragment *hfrag,
                                  Eterm reason, Eterm token);

/**
 *
 * @brief Send an unlink signal to a process.
 *
 * This function is used instead of erts_proc_sig_send_unlink()
 * when the signal arrives via the distribution and
 * therefore no link structure is available.
 *
 * @param[in]     dep           Distribution entry of channel
 *                              that the signal arrived on.
 *
 * @param[in]     from          Identifier of sender.
 *
 * @param[in]     to            Identifier of receiver.
 *
 */
void
erts_proc_sig_send_dist_unlink(struct dist_entry_ *dep,
                               Eterm from, Eterm to);

/**
 *
 * @brief Send a monitor down signal to a process.
 *
 * @param[in]     mon           Pointer to target monitor
 *                              structure from the sending
 *                              side. It should contain
 *                              information about receiver.
 *
 * @param[in]     reason        Exit reason.
 *
 */
void
erts_proc_sig_send_monitor_down(ErtsMonitor *mon, Eterm reason);

/**
 *
 * @brief Send a demonitor signal to a process.
 *
 * @param[in]     mon           Pointer to origin monitor
 *                              structure from the sending
 *                              side. It should contain
 *                              information about receiver.
 *
 * @param[in]     reason        Exit reason.
 *
 */
void
erts_proc_sig_send_demonitor(ErtsMonitor *mon);

/**
 *
 * @brief Send a monitor signal to a process.
 *
 * @param[in]     mon           Pointer to target monitor
 *                              structure to insert on
 *                              receiver side.
 *
 * @param[in]     to            Identifier of receiver.
 *
 * @return                      A non-zero value if
 *                              signal was successfully
 *                              sent. If a zero, value
 *                              the signal was not sent
 *                              due to the receiver not
 *                              existing. The sender
 *                              needs to deallocate the
 *                              monitor structure.
 *
 */
int
erts_proc_sig_send_monitor(ErtsMonitor *mon, Eterm to);

/**
 *
 * @brief Send a monitor down signal to a process.
 *
 * This function is used instead of erts_proc_sig_send_monitor_down()
 * when the signal arrives via the distribution and
 * therefore no monitor structure is available.
 *
 * @param[in]     dep           Pointer to distribution entry
 *                              of channel that the signal
 *                              arrived on.
 *
 * @param[in]     ref           Reference identifying the monitor.
 *
 * @param[in]     from          Identifier of sender.
 *
 * @param[in]     to            Identifier of receiver.
 *
 * @param[in]     dist_ext      The exit reason in external term format
 *
 * @param[in]     hfrag         Heap frag with trace token and dist_ext
 *                              iff available, otherwise NULL.
 *
 * @param[in]     reason        Exit reason.
 *
 */
void
erts_proc_sig_send_dist_monitor_down(DistEntry *dep, Eterm ref,
                                     Eterm from, Eterm to,
                                     ErtsDistExternal *dist_ext,
                                     ErlHeapFragment *hfrag,
                                     Eterm reason);

/**
 *
 * @brief Send a demonitor signal to a process.
 *
 * This function is used instead of erts_proc_sig_send_demonitor()
 * when the signal arrives via the distribution and
 * no monitor structure is available.
 *
 * @param[in]     to            Identifier of receiver.
 *
 * @param[in]     ref           Reference identifying the monitor.
 *
 */
void
erts_proc_sig_send_dist_demonitor(Eterm to, Eterm ref);

/**
 *
 * @brief Send a persistent monitor triggered signal to a process.
 *
 * Used by monitors that are not auto disabled such as for
 * example 'time_offset' monitors.
 *
 * @param[in]     type          Monitor type.
 *
 * @param[in]     key           Monitor key.
 *
 * @param[in]     from          Identifier of sender.
 *
 * @param[in]     to            Identifier of receiver.
 *
 * @param[in]     msg           Message template.
 *
 * @param[in]     msg_sz        Heap size of message template.
 *
 */
void
erts_proc_sig_send_persistent_monitor_msg(Uint16 type, Eterm key,
                                          Eterm from, Eterm to,
                                          Eterm msg, Uint msg_sz);

/**
 *
 * @brief Send a trace change signal to a process.
 *
 * @param[in]     to            Identifier of receiver.
 *
 * @param[in]     on            Trace flags to enable.
 *
 * @param[in]     off           Trace flags to disable.
 *
 * @param[in]     tracer        Tracer to set. If the non-value,
 *                              tracer will not be changed.
 *
 */
void
erts_proc_sig_send_trace_change(Eterm to, Uint on, Uint off,
                                Eterm tracer);

/**
 *
 * @brief Send a group leader signal to a process.
 *
 * Set group-leader of receiving process. If sent locally,
 * a response message '{Ref, Result}' is sent to the original
 * sender when performed where Ref is the reference passed
 * as 'ref' argument, and Result is either 'true' or 'badarg'.
 *
 * @param[in]     c_p           Pointer to process struct of
 *                              currently executing process.
 *                              NULL if signal arrived via
 *                              distribution.
 *
 * @param[in]     to            Identifier of receiver.
 *
 * @param[in]     gl            Identifier of new group leader.
 *
 * @param[in]     ref           Reference to use in response
 *                              message to locally sending
 *                              process (i.e., c_p when c_p
 *                              is non-null).
 *
 */
void
erts_proc_sig_send_group_leader(Process *c_p, Eterm to, Eterm gl,
                                Eterm ref);

/**
 *
 * @brief Send an 'is process alive' signal to a process.
 *
 * A response message '{Ref, Result}' is sent to the
 * sender when performed where Ref is the reference passed
 * as 'ref' argument, and Result is either 'true' or 'false'.
 *
 * @param[in]     c_p           Pointer to process struct of
 *                              currently executing process.
 *                              NULL if signal arrived via
 *                              distribution.
 *
 * @param[in]     to            Identifier of receiver.
 *
 * @param[in]     ref           Reference to use in response
 *                              message to the sending
 *                              process (i.e., c_p).
 *
 */
void
erts_proc_sig_send_is_alive_request(Process *c_p, Eterm to,
                                    Eterm ref);

/**
 *
 * @brief Send a 'process info request' signal to a process.
 *
 * A response message '{Ref, Result}' is sent to the
 * sender when performed where Ref is the reference passed
 * as 'ref' argument, and Result corresponds to return result
 * from erlang:process_info/[1,2].
 *
 * @param[in]     c_p           Pointer to process struct of
 *                              currently executing process.
 *                              NULL if signal arrived via
 *                              distribution.
 *
 * @param[in]     to            Identifier of receiver.
 *
 * @param[in]     item_ix       Info index array to pass to
 *                              erts_process_info()
 *
 * @param[in]     len           Lenght of info index array
 *
 * @param[in]     need_msgq_len Non-zero if message queue
 *                              length is needed; otherwise,
 *                              zero. If non-zero, sig_qs.len
 *                              will be set to correspond
 *                              to the message queue length
 *                              before call to
 *                              erts_process_info()
 *
 * @param[in]     flags         Flags to pass to
 *                              erts_process_info()
 *
 * @param[in]     reserve_size  Heap size that is known to
 *                              be needed. May not be correct
 *                              though.
 *
 * @param[in]     ref           Reference to use in response
 *                              message to the sending
 *                              process (i.e., c_p).
 *
 */
int
erts_proc_sig_send_process_info_request(Process *c_p,
                                        Eterm to,
                                        int *item_ix,
                                        int len,
                                        int need_msgq_len,
                                        int flags,
                                        Uint reserve_size,
                                        Eterm ref);

/**
 *
 * @brief Send a 'sync suspend' signal to a process.
 *
 * A response message '{Tag, Reply}' is sent to the
 * sender when performed where Tag is the term passed
 * as 'tag' argument. Reply is either 'suspended',
 * 'not_suspended', 'exited' if the operation is
 * asynchronous; otherwise, the 'reply' argument or
 * 'badarg' if process terminated.
 *
 * This signal does *not* change the suspend state, only
 * reads and reply the state. This signal is typically
 * sent after a suspend request (monitor of suspend type)
 * signal has been sent to the process in order to get a
 * response when the suspend monitor has been processed.
 *
 * @param[in]     c_p           Pointer to process struct of
 *                              currently executing process.
 *
 * @param[in]     to            Identifier of receiver.
 *
 * @param[in]     tag           Tag to use in response
 *                              message to the sending
 *                              process (i.e., c_p).
 *
 * @param[in]     reply         Reply to send if this
 *                              is a synchronous operation;
 *                              otherwise, THE_NON_VALUE.
 */
void
erts_proc_sig_send_sync_suspend(Process *c_p, Eterm to,
                                Eterm tag, Eterm reply);

/**
 *
 * @brief Send an 'rpc' signal to a process.
 *
 * The function 'func' will be executed in the
 * context of the receiving process. A response
 * message '{Ref, Result}' is sent to the sender
 * when 'func' has been called. 'Ref' is the reference
 * returned by this function and 'Result' is the
 * term returned by 'func'. If the return value of
 * 'func' is not an immediate term, 'func' has to
 * allocate a heap fragment where the result is stored
 * and update the the heap fragment pointer pointer
 * passed as third argument to point to it.
 *
 * If this function returns a reference, 'func' will
 * be called in the context of the receiver. However,
 * note that this might happen when the receiver is in
 * an exiting state. The caller of this function
 * *unconditionally* has to enter a receive that match
 * on the returned reference in all clauses as next
 * receive; otherwise, bad things will happen!
 *
 * If THE_NON_VALUE is returned, the receiver did not
 * exist. The signal was not sent, and no specific
 * receive has to be entered by the caller.
 *
 * @param[in]     c_p           Pointer to process struct of
 *                              currently executing process.
 *
 * @param[in]     to            Identifier of receiver process.
 *
 * @param[in]     reply         Non-zero if a reply is wanted.
 *
 * @param[in]     func          Function to execute in the
 *                              context of the receiver.
 *                              First argument will be a
 *                              pointer to the process struct
 *                              of the receiver process.
 *                              Second argument will be 'arg'
 *                              (see below). Third argument
 *                              will be a pointer to a pointer
 *                              to a heap fragment for storage
 *                              of result returned from 'func'
 *                              (i.e. an 'out' parameter).
 *
 * @param[in]     arg           Void pointer to argument
 *                              to pass as second argument
 *                              in call of 'func'.
 *
 * @returns                     If the request was sent,
 *                              an internal ordinary
 *                              reference; otherwise,
 *                              THE_NON_VALUE (non-existing
 *                              receiver).
 */
Eterm
erts_proc_sig_send_rpc_request(Process *c_p,
                               Eterm to,
                               int reply,
                               Eterm (*func)(Process *, void *, int *, ErlHeapFragment **),
                               void *arg);

/*
 * End of send operations of currently supported process signals.
 */


/**
 *
 * @brief Handle incoming signals.
 *
 * Called by an ordinary scheduler in order to handle incoming
 * signals for a process. The work is done on the middle part
 * of the signal queue. The maximum amount of signals handled
 * is limited by the amount of reductions given when calling.
 * Note that a reduction does not necessarily map to a signal.
 *
 * @param[in]     c_p           Pointer to process struct of
 *                              currently executing process.
 *
 * @param[out]    statep        Pointer to process state after
 *                              signal handling. May not be NULL.
 *
 * @param[in,out] redsp         Pointer to an integer containing
 *                              reductions. On input, the amount
 *                              of preferred reductions to be
 *                              used by the call. On output, the
 *                              amount of reductions consumed.
 *
 * @param[in]     max_reds      Absolute maximum of reductions
 *                              to use. If the process cannot
 *                              make progress after the preferred
 *                              amount of reductions has been
 *                              consumed, signal handling may
 *                              proceed up to a maximum of
 *                              'max_reds' in order to make
 *                              the process able to proceed
 *                              with other tasks after handling
 *                              has finished.
 *
 * @param[in]     local_only    If is zero, new signals may be
 *                              fetched from the outer queue and
 *                              put in the middle queue before
 *                              signal handling is performed. If
 *                              non-zero, no new signals will be
 *                              fetched before handling begins.
 *
 * @return                      Returns a non-zero value, when
 *                              no more signals to handle in the
 *                              middle queue remain. A zero
 *                              return value means that there
 *                              remains signals in the middle
 *                              queue.
 */
int
erts_proc_sig_handle_incoming(Process *c_p, erts_aint32_t *statep,
                              int *redsp, int max_reds,
                              int local_only);

/**
 *
 * @brief Handle remaining signals for an exiting process
 *
 * Called as part of termination of a process. It will handle
 * remaining signals.
 *
 * @param[in]     c_p           Pointer to process struct of
 *                              currently executing process.
 *
 * @param[in,out] redsp         Pointer to an integer containing
 *                              reductions. On input, the amount
 *                              of maximum reductions to be
 *                              used by the call. On output, the
 *                              amount of reductions consumed.
 *
 * @return                      Returns a non-zero value, when
 *                              no more signals to handle in the
 *                              middle queue remain. A zero
 *                              return value means that there
 *                              remains signals in the middle
 *                              queue.
 */
int
erts_proc_sig_handle_exit(Process *c_p, Sint *redsp);

/**
 *
 * @brief Helper for loop_rec instruction.
 *
 * This function should only be called from the loop_rec
 * instruction (or equivalents). It is called when loop_rec
 * reach the end of the inner queue (which is the only
 * part of the signal queue that receive is allowed to
 * operate on). When called, this function tries to make
 * more messages available in the inner queue. This by
 * fetching signals from the outer queue to the middle
 * queue and/or processing signals in the middle queue.
 *
 * @param[in]   c_p             Pointer to process struct of
 *                              currently executing process.
 *
 * @param[in]   fcalls          Content of FCALLS in
 *                              process_main()
 *
 * @param[in]   neg_o_reds      Content of neg_o_reds in
 *                              process_main()
 *
 * @param[out]  msgpp           Pointer to pointer to next
 *                              available message to process.
 *                              If *msgpp == NULL, no more
 *                              messages are available.
 *
 * @param[out]  get_outp        Pointer to an integer
 *                              indicating how to respond
 *                              if no more messages are
 *                              available (msgpp). If integer
 *                              is set to zero, loop_rec
 *                              should jump to an appropriate
 *                              wait instruction. If zero,
 *                              the message queue lock remain
 *                              locked since the test for
 *                              more messages was done.
 *                              If the integer is set to a
 *                              value larger that zero, the
 *                              process exited. If the integer
 *                              is set to a value less than
 *                              zero, the process is required
 *                              to yield.
 *
 *
 * @return                      The amount of reductions
 *                              consumed.
 *
 */
int
erts_proc_sig_receive_helper(Process *c_p, int fcalls,
                             int neg_o_reds, ErtsMessage **msgpp,
                             int *get_outp);

/**
 *
 * @brief Fetch signals from the outer queue
 *
 * Fetches signals from outer queue and places them in the
 * middle queue ready for signal handling. If the middle
 * queue is empty, only message signals were present in the
 * outer queue, and no receive tracing has been enabled on
 * the process, the middle queue is bypassed and messages
 * are delivered directly to the inner queue instead.
 *
 * @param[in]   c_p             Pointer to process struct of
 *                              currently executing process.
 * @returns                     Amount of message signals in
 *                              inner plus middle signal
 *                              queues after fetch completed
 *                              (NOT the message queue
 *                              length).
 */
ERTS_GLB_INLINE Sint erts_proc_sig_fetch(Process *p);

/**
 *
 * @brief Get amount of messages in private queues
 *
 * @param[in]   c_p             Pointer to process struct of
 *                              currently executing process.
 *
 * @returns                     Amount of message signals in
 *                              inner plus middle signal
 *                              queues after fetch completed
 *                              (NOT the message queue
 *                              length).
 */
Sint
erts_proc_sig_privqs_len(Process *c_p);


/**
 * @brief Enqueue list of signals on process.
 *
 * Message queue must be locked on receiving process.
 *
 * @param rp                Receiving process.
 * @param first             First signal in list.
 * @param last              Last signal in list.
 * @param last_next         Pointer to next-pointer to last non-message signal
 *                          or NULL if no non-message signal after 'first'.
 * @param msg_cnt           Number of message signals in list.
 * @param in_state          'state' of rp.
 *
 * @return                  'state' of rp.
 */
erts_aint32_t
erts_enqueue_signals(Process *rp, ErtsMessage *first,
                     ErtsMessage **last, ErtsMessage **last_next,
                     Uint msg_cnt,
                     erts_aint32_t in_state);

/**
 *
 * @brief Flush pending signal.
 *
 */
void
erts_proc_sig_send_pending(ErtsSchedulerData* esdp);

/**
 *
 * @brief Schedule process to handle enqueued signal(s).
 *
 * @param rp                Receiving process.
 * @param state             'state' of rp.
 * @param enable_flag       Additional state flags to enable, like
 *                          ERTS_PSFLG_ACTIVE if message has been enqueued.
 */
ERTS_GLB_INLINE void erts_proc_notify_new_sig(Process* rp, erts_aint32_t state,
                                              erts_aint32_t enable_flag);

void erts_make_dirty_proc_handled(Eterm pid, erts_aint32_t state,
                                  erts_aint32_t prio);


typedef struct {
    Uint size;
    ErtsMessage *msgp;
} ErtsMessageInfo;

/**
 *
 * @brief Prepare signal queue for inspection by process_info()
 *
 *
 * @param[in]   c_p             Pointer to process struct of
 *                              currently executing process.
 *
 * @param[in]   rp              Pointer to process struct of
 *                              process to inspect.
 *
 * @param[in]   rp_locks        Process locks held on 'rp'.
 *
 * @param[in]   info_on_self    Integer set to non-zero value
 *                              if caller is inspecting itself;
 *                              otherwise, zero.
 *
 * @param[in]   mip             Pointer to array of
 *                              ErtsMessageInfo structures.
 */
Uint erts_proc_sig_prep_msgq_for_inspection(Process *c_p,
                                            Process *rp,
                                            ErtsProcLocks rp_locks,
                                            int info_on_self,
                                            ErtsMessageInfo *mip);

/**
 *
 * @brief Move message data of messages in private queues to heap
 *
 * Move message data of messages in private queues to the heap.
 * This is part of GC of processes that uses on-heap message
 * data.
 *
 * @param[in]   c_p             Pointer to process struct of
 *                              currently executing process.
 *
 */
void erts_proc_sig_move_msgs_to_heap(Process *c_p);

/**
 *
 * @brief Size of signal in bytes.
 *
 * @param[in]   sig             Signal to inspect.
 *
 */
Uint erts_proc_sig_signal_size(ErtsSignal *sig);


/**
 *
 * @brief Clear seq trace tokens on all signals
 *
 * Assumes thread progress has been blocked!
 *
 * @param[in]   c_p             Pointer to process
 *
 */
void
erts_proc_sig_clear_seq_trace_tokens(Process *c_p);

/**
 *
 * @brief Handle pending suspend requests
 *
 * Should be called by processes when they stop
 * execution on a dirty scheduler if they have
 * pending suspend requests (i.e. when
 * ERTS_PROC_GET_PENDING_SUSPEND(c_p) != NULL).
 *
 * @param[in]   c_p             Pointer to executing
 *                              process
 */
void
erts_proc_sig_handle_pending_suspend(Process *c_p);

/**
 *
 * @brief Decode the reason term in an external signal
 *
 * Any distributed signal with a payload only has the control
 * message decoded by the dist entry. The final decode of the
 * payload is done by the process when it inspects the signal
 * by calling this function.
 *
 * This functions handles both messages and link/monitor exits.
 *
 * Return true if the decode was successful, false otherwise.
 *
 * @param[in]   c_p             Pointer to executing process
 *
 * @param[in]   proc_lock       Locks held by process. Should always be MAIN.
 *
 * @param[in]   msgp            The signal to decode
 *
 * @param[in]   force_off_heap  If the term should be forced to be off-heap
 */
int
erts_proc_sig_decode_dist(Process *proc, ErtsProcLocks proc_locks,
                          ErtsMessage *msgp, int force_off_heap);

ErtsDistExternal *
erts_proc_sig_get_external(ErtsMessage *msgp);

/**
 * @brief Initialize this functionality
 */
void erts_proc_sig_queue_init(void);

void
erts_proc_sig_debug_foreach_sig(Process *c_p,
                                void (*msg_func)(ErtsMessage *, void *),
                                void (*oh_func)(ErlOffHeap *, void *),
                                ErtsMonitorFunc mon_func,
                                ErtsLinkFunc lnk_func,
                                void (*ext_func)(ErtsDistExternal *, void *),
                                void *arg);

extern Process *erts_dirty_process_signal_handler;
extern Process *erts_dirty_process_signal_handler_high;
extern Process *erts_dirty_process_signal_handler_max;

void erts_proc_sig_fetch__(Process *proc);
Sint erts_proc_sig_fetch_msgq_len_offs__(Process *proc);

#if ERTS_GLB_INLINE_INCL_FUNC_DEF

ERTS_GLB_INLINE Sint
erts_proc_sig_fetch(Process *proc)
{
    Sint res = 0;
    ErtsSignal *sig;

    ERTS_LC_ASSERT(ERTS_PROC_IS_EXITING(proc)
                   || ((erts_proc_lc_my_proc_locks(proc)
                        & (ERTS_PROC_LOCK_MAIN
                           | ERTS_PROC_LOCK_MSGQ))
                       == (ERTS_PROC_LOCK_MAIN
                           | ERTS_PROC_LOCK_MSGQ)));

    ERTS_HDBG_CHECK_SIGNAL_IN_QUEUE(proc);
    ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(proc, !0);

    sig = (ErtsSignal *) proc->sig_inq.first;
    if (sig) {
        if (ERTS_LIKELY(sig->common.tag != ERTS_PROC_SIG_MSGQ_LEN_OFFS_MARK))
            erts_proc_sig_fetch__(proc);
        else
            res = erts_proc_sig_fetch_msgq_len_offs__(proc);
    }

    res += proc->sig_qs.len;

    ERTS_HDBG_CHECK_SIGNAL_PRIV_QUEUE(proc, !0);

#ifdef ERTS_PROC_SIG_HARD_DEBUG_SIGQ_MSG_LEN
    {
        Sint len = 0;
        ERTS_FOREACH_SIG_PRIVQS(
            proc, mp,
            {
                if (ERTS_SIG_IS_MSG(mp))
                    len++;
            });
        ERTS_ASSERT(res == len);
    }
#endif

    return res;
}

ERTS_GLB_INLINE void
erts_proc_notify_new_sig(Process* rp, erts_aint32_t state,
                         erts_aint32_t enable_flag)
{
    if (~(state & (ERTS_PSFLG_EXITING
                   | ERTS_PSFLG_ACTIVE_SYS
                   | ERTS_PSFLG_SIG_IN_Q))
        | (~state & enable_flag)) {
        /* Schedule process... */
        state = erts_proc_sys_schedule(rp, state, enable_flag);
    }

    if (state & (ERTS_PSFLG_DIRTY_RUNNING
                 | ERTS_PSFLG_DIRTY_RUNNING_SYS)) {
        erts_make_dirty_proc_handled(rp->common.id, state, -1);
    }
}

#endif /* ERTS_GLB_INLINE_INCL_FUNC_DEF */

#endif /* ERTS_PROC_SIG_QUEUE_H__ */