-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathgasnet_help.h
More file actions
2272 lines (2056 loc) · 102 KB
/
Copy pathgasnet_help.h
File metadata and controls
2272 lines (2056 loc) · 102 KB
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
/* $Source: github.com:BerkeleyLab/gasnet.git/gasnet_help.h $
* Description: GASNet Header Helpers (Internal code, not for client use)
* Copyright 2002, Dan Bonachea <bonachea@cs.berkeley.edu>
* Terms of use are as specified in license.txt
*/
#ifndef _IN_GASNETEX_H
#error This file is not meant to be included directly- clients should include gasnetex.h
#endif
#ifndef _GASNET_HELP_H
#define _GASNET_HELP_H
typedef struct {
uint64_t allocated_bytes; /* num bytes ever allocated */
uint64_t freed_bytes; /* num bytes ever freed */
uint64_t live_bytes; /* num bytes currently allocated */
uint64_t live_bytes_max; /* max num bytes live at any given time */
uint64_t allocated_objects; /* num objects ever allocated */
uint64_t freed_objects; /* num objects ever freed */
uint64_t live_objects; /* num objects currently allocated */
uint64_t live_objects_max; /* max num objects live at any given time */
uint64_t overhead_bytes; /* num bytes consumed by allocator overhead (lower bound) */
} gasneti_heapstats_t;
#if GASNET_DEBUGMALLOC
/* _curloc is passed to debug mallocator as "file:line",
or the special constant "SRCPOS" to retrieve the info from gasnet_srclines
To enable use of srcpos for a compilation unit, client should:
#undef GASNETT_MALLOC_USE_SRCPOS
#define GASNETT_MALLOC_USE_SRCPOS 1
*/
#ifndef GASNETT_MALLOC_USE_SRCPOS
#define GASNETT_MALLOC_USE_SRCPOS 0 /* off by default */
#endif
#define GASNETI_CURLOCFARG , const char *_curloc
#define GASNETI_CURLOCAARG , (GASNETT_MALLOC_USE_SRCPOS ? \
"SRCPOS" : \
__FILE__ ":" _STRINGIFY(__LINE__))
#define GASNETI_CURLOCPARG , _curloc
extern size_t _gasneti_memcheck(void *_ptr, const char *_curloc, int _checktype);
extern void _gasneti_memcheck_one(const char *_curloc);
extern void _gasneti_memcheck_all(const char *_curloc);
#define gasneti_memcheck(ptr) (gasneti_assert(ptr != NULL), \
(void)_gasneti_memcheck(ptr, __FILE__ ":" _STRINGIFY(__LINE__), 0))
#define gasneti_memcheck_one() _gasneti_memcheck_one(__FILE__ ":" _STRINGIFY(__LINE__))
#define gasneti_memcheck_all() _gasneti_memcheck_all(__FILE__ ":" _STRINGIFY(__LINE__))
extern int gasneti_getheapstats(gasneti_heapstats_t *_pstat);
extern void gasneti_heapinfo_dump(const char *_filename, int _show_live_objects);
#else
#define GASNETI_CURLOCFARG
#define GASNETI_CURLOCAARG
#define GASNETI_CURLOCPARG
#define gasneti_memcheck(ptr) ((void)0)
#define gasneti_memcheck_one() ((void)0)
#define gasneti_memcheck_all() ((void)0)
#define gasneti_getheapstats(pstat) (memset(pstat, 0, sizeof(gasneti_heapstats_t)),1)
#define gasneti_heapinfo_dump(f,s) ((void)0)
#endif
/* extern versions of gasnet malloc fns for use in public headers */
extern void *_gasneti_extern_malloc(size_t _sz GASNETI_CURLOCFARG) GASNETI_MALLOC;
GASNETI_MALLOCP(_gasneti_extern_malloc)
extern void *_gasneti_extern_realloc(void *_ptr, size_t _sz GASNETI_CURLOCFARG);
extern void *_gasneti_extern_calloc(size_t _n, size_t _s GASNETI_CURLOCFARG) GASNETI_MALLOC;
GASNETI_MALLOCP(_gasneti_extern_calloc)
extern void _gasneti_extern_free(void *_ptr GASNETI_CURLOCFARG);
extern void _gasneti_extern_leak(void *_ptr GASNETI_CURLOCFARG);
extern char *_gasneti_extern_strdup(const char *_s GASNETI_CURLOCFARG) GASNETI_MALLOC;
GASNETI_MALLOCP(_gasneti_extern_strdup)
extern char *_gasneti_extern_strndup(const char *_s, size_t _n GASNETI_CURLOCFARG) GASNETI_MALLOC;
GASNETI_MALLOCP(_gasneti_extern_strndup)
#define gasneti_extern_malloc(sz) _gasneti_extern_malloc((sz) GASNETI_CURLOCAARG)
#define gasneti_extern_realloc(ptr,sz) _gasneti_extern_realloc((ptr), (sz) GASNETI_CURLOCAARG)
#define gasneti_extern_calloc(N,S) _gasneti_extern_calloc((N),(S) GASNETI_CURLOCAARG)
#define gasneti_extern_free(ptr) _gasneti_extern_free((ptr) GASNETI_CURLOCAARG)
#define gasneti_extern_leak(ptr) _gasneti_extern_leak((ptr) GASNETI_CURLOCAARG)
#define gasneti_extern_strdup(s) _gasneti_extern_strdup((s) GASNETI_CURLOCAARG)
#define gasneti_extern_strndup(s,n) _gasneti_extern_strndup((s),(n) GASNETI_CURLOCAARG)
/* aligned malloc - allocated size bytes with given power-of-2 alignment
may only be freed using gasneti_free_aligned */
#if HAVE_POSIX_MEMALIGN && !GASNET_DEBUGMALLOC
// when debug mallocator is disabled, posix_memalign is friendlier
// for use with heap analysis tools like valgrind
#define GASNETI_USE_POSIX_MEMALIGN 1
#endif
GASNETI_INLINE(_gasneti_malloc_aligned) GASNETI_MALLOC
void * _gasneti_malloc_aligned(size_t _alignment, size_t _size GASNETI_CURLOCFARG) {
gasneti_assert(GASNETI_POWEROFTWO(_alignment));
gasneti_assert(_alignment <= GASNET_PAGESIZE);
#if GASNETI_USE_POSIX_MEMALIGN
if_pf(_alignment < sizeof(void*)) _alignment = sizeof(void*);
void *_result = NULL; // init to avoid -Wmaybe-uninitialized warnings
int _return_code = posix_memalign(&_result, _alignment, _size);
gasneti_assert_zeroret(_return_code);
#else
size_t _alloc_size = _size + sizeof(void *) + _alignment;
void *_base = _gasneti_extern_malloc(_alloc_size GASNETI_CURLOCPARG);
void **_result = (void **)GASNETI_ALIGNUP((uintptr_t)_base + sizeof(void *), _alignment);
*(_result - 1) = _base; /* hidden base ptr for free() */
gasneti_assert_ptr((void *)(_result - 1) ,>=, _base);
gasneti_assert_ptr(((uint8_t *)_result + _size) ,<=, ((uint8_t *)_base + _alloc_size));
#endif
gasneti_assume(_result);
gasneti_assert_ptr(_result ,==, (void **)GASNETI_ALIGNUP(_result, _alignment));
return (void *)_result;
}
GASNETI_MALLOCP(_gasneti_malloc_aligned)
#define gasneti_malloc_aligned(align,sz) _gasneti_malloc_aligned((align), (sz) GASNETI_CURLOCAARG)
GASNETI_INLINE(_gasneti_free_aligned)
void _gasneti_free_aligned(void *_ptr GASNETI_CURLOCFARG) {
gasneti_assert(_ptr);
#if GASNETI_USE_POSIX_MEMALIGN
free(_ptr);
#else
void *_base = *((void **)_ptr - 1);
gasneti_assert(_base);
_gasneti_extern_free(_base GASNETI_CURLOCPARG);
#endif
}
#define gasneti_free_aligned(ptr) _gasneti_free_aligned((ptr) GASNETI_CURLOCAARG)
GASNETI_INLINE(_gasneti_leak_aligned)
void _gasneti_leak_aligned(void *_ptr GASNETI_CURLOCFARG) {
gasneti_assert(_ptr);
#if !GASNETI_USE_POSIX_MEMALIGN
void *_base = *((void **)_ptr - 1);
gasneti_assert(_base);
_gasneti_extern_leak(_base GASNETI_CURLOCPARG);
#endif
}
#define gasneti_leak_aligned(ptr) _gasneti_leak_aligned((ptr) GASNETI_CURLOCAARG)
extern const char *gasnet_max_segsize_str; // client-overrideable max segment size
extern uint64_t gasnet_max_segsize; // DEPRECATED: client-overrideable max segment size
#if GASNET_DEBUG
extern void gasneti_checkinit(void);
extern void gasneti_checkattach(void);
#define GASNETI_CHECKINIT() gasneti_checkinit()
#define GASNETI_CHECKATTACH() gasneti_checkattach()
#else
#define GASNETI_CHECKINIT() ((void)0)
#define GASNETI_CHECKATTACH() ((void)0)
#endif
extern gex_Rank_t gasneti_mynode;
#define gex_System_QueryJobRank() (GASNETI_CHECKINIT(), (gex_Rank_t)gasneti_mynode)
extern gex_Rank_t gasneti_nodes;
#define gex_System_QueryJobSize() (GASNETI_CHECKINIT(), (gex_Rank_t)gasneti_nodes)
/* ------------------------------------------------------------------------------------ */
extern int gasneti_VerboseErrors;
#define gex_System_GetVerboseErrors() ((int)gasneti_VerboseErrors)
GASNETI_INLINE(gex_System_SetVerboseErrors)
void gex_System_SetVerboseErrors(int _enable) {
gasneti_assert(_enable == 1 || _enable == 0);
gasneti_VerboseErrors = _enable;
}
/* ------------------------------------------------------------------------------------ */
#if GASNETI_TM0_ALIGN
// We can detect TM0 by its better alignment than other tm's
GASNETI_INLINE(gasneti_is_tm0)
int gasneti_is_tm0(gasneti_TM_t _i_tm) {
gasneti_static_assert(GASNETI_POWEROFTWO(GASNETI_TM0_ALIGN));
gasneti_static_assert(GASNETI_TM0_ALIGN > 1);
return (!((uintptr_t)(_i_tm) & (GASNETI_TM0_ALIGN-1)));
}
#else
// Cannot use alignment to distinguish TM0
extern gasneti_TM_t gasneti_thing_that_goes_thunk_in_the_dark;
#define gasneti_is_tm0(_i_tm) ((_i_tm) == gasneti_thing_that_goes_thunk_in_the_dark)
#endif
// "TM-pair"
//
// We have one external handle type `gex_TM_t`, but two corresponding internal
// types: `gasneti_TM_t` and `gasneti_TM_Pair_t`. A `gasneti_TM_t` can be
// either an actual pointer to a `struct gasneti_team_member_internal_s` (with
// fields describing the TM) OR it can be just a 32-bit inline representation
// of a TM-pair (with fields accessed via `gasneti_tm_pair_{loc,rem}_idx()`).
//
// Use of the query `gasneti_e_tm_is_pair()` reports whether a `gex_TM_t`, once
// imported, will have the TM-pair representation.
// Use of the query `gasneti_i_tm_is_pair()` reports whether a `gasneti_TM_t`
// has the TM-pair representation.
//
// Code not needing to access any fields may pass `gex_TM_t` or `gasneti_TM_t`
// values transparently, though the latter is preferred so that the debug check
// for the "MAGIC" signature upon import occurs early in the call stack. Such
// code does not require any explicit handling of TM-pairs.
//
// For code which does access fields, there are three approaches illustrated
// by the following examples in which
// + `e_tm` is a `gex_TM_t`
// + `i_tm` is a `gasneti_TM_t` without certainty of contents
// + `my_tm` is a `gasneti_TM_t` certain to point to a structure
// + `my_pair` is a `gasneti_TM_Pair_t` certain to hold a 32-bit inline pair
//
// 1. "import excluding pair" - for code paths where TM-pair is prohibited:
// my_tm = gasneti_import_tm_nonpair(e_tm);
//
// 2. "check then import"
// if (gasneti_e_tm_is_pair(e_tm))
// my_pair = gasneti_import_tm_pair(e_tm);
// else
// my_tm = gasneti_import_tm(e_tm);
//
// 3. "import then check before use"
// i_tm = gasneti_import_tm(e_tm)
// [...]
// if (gasneti_i_tm_is_pair(i_tm))
// my_pair = gasneti_i_tm_to_pair(i_tm);
// else
// my_tm = i_tm;
//
// The four type-conversion functions used in the examples above:
// + gasneti_TM_t gasneti_import_tm(gex_TM_t);
// Does not check if the argument is a TM-pair
// + gasneti_TM_t gasneti_import_tm_nonpair(gex_TM_t);
// Asserts that the argument is NOT a TM-pair
// + gasneti_TM_Pair_t gasneti_import_tm_pair(gex_TM_t);
// Asserts that the argument is a TM-pair
// + gasneti_TM_Pair_t gasneti_i_tm_to_pair(gasneti_TM_t);
// Asserts that the argument is a TM-pair
//
// -OR-
//
// At least the following internal functions operate an `e_tm` or `i_tm`, with
// transparent support for both encodings and may be sufficient to keep much
// code independent of TM-pairness:
// + gasneti_[ei]_tm_rank_to_jobrank()
// + gasneti_[ei]_tm_rank_to_ep_index()
// + gasneti_[ei]_tm_rank_to_location()
// + gasneti_[ei]_tm_jobrank_to_rank()
// + gasneti_[ei]_tm_size()
// Returns gex_System_QueryJobSize() for TM-pair, suitable for range checking a rank
// Generalize/replace the following idioms which do not accept a TM-pair:
// `gex_TM_QuerySize(e_tm)`
// `gasneti_import_tm(e_tm)->_size`
// `i_tm->_size`
// + gasneti_[ei]_tm_to_i_ep()
// Generalize/replace the following idioms which do not accept a TM-pair:
// `gex_TM_QueryEP(e_tm)`
// `gasneti_import_tm(e_tm)->_ep`
// `i_tm->_ep`
// + gasneti_[ei]_tm_to_ep_index()
// More efficient replacement for `gasneti_[ei]_tm_to_i_ep()->_index`,
// replacing multiple alternatives which do not accept a TM-pair
// + gasneti_boundscheck()
// + gasneti_formattm()
// + gasneti_pshm_local_rank()
// + gasneti_pshm_in_supernode()
// + gasneti_pshm_addr2local()
// + GASNETI_NBRHD_MAPPED()
// + GASNETI_NBRHD_MAPPED_ADDR()
// + GASNETI_NBRHD_MAPPED_ADDR_OR_NULL()
// + gasneti_jobrank_if_mappable()
// + gasnete_mapped_at()
#if GASNET_DEBUG
GASNETI_INLINE(gasneti_assertvalid_tm_pair)
void gasneti_assertvalid_tm_pair(gasneti_TM_Pair_t _tm_pair) {
// TODO: check that client index (currently always zero) is in range
gasneti_assert_uint((_tm_pair & 0xff) ,==, 1);
}
#else
#define gasneti_assertvalid_tm_pair(_tm_pair) ((void)0)
#endif
// Detect a TM generated by gex_TM_Pair
GASNETI_INLINE(gasneti_i_tm_is_pair)
int gasneti_i_tm_is_pair(gasneti_TM_t _i_tm)
{
gasneti_TM_Pair_t _tm_pair = (gasneti_TM_Pair_t) _i_tm;
int _result = (_tm_pair & 1);
if (_result) gasneti_assertvalid_tm_pair(_tm_pair);
return _result;
}
#define gasneti_e_tm_is_pair(_e_tm) gasneti_i_tm_is_pair(gasneti_import_tm(_e_tm))
// Assertion-checking conversions from {gex,gasneti}_TM_t to gasneti_TM_Pair_t
GASNETI_INLINE(gasneti_i_tm_to_pair)
gasneti_TM_Pair_t gasneti_i_tm_to_pair(gasneti_TM_t _i_tm)
{
gasneti_TM_Pair_t _tm_pair = (gasneti_TM_Pair_t) _i_tm;
gasneti_assertvalid_tm_pair(_tm_pair);
return _tm_pair;
}
#define gasneti_e_tm_to_pair(_e_tm) gasneti_i_tm_to_pair(gasneti_import_tm(_e_tm))
// Extract EP indices from a known TM_Pair
GASNETI_INLINE(gasneti_tm_pair_loc_idx)
gex_EP_Index_t gasneti_tm_pair_loc_idx(gasneti_TM_Pair_t _tm_pair)
{
gasneti_assertvalid_tm_pair(_tm_pair);
return (_tm_pair >> GASNETI_TM_PAIR_LOC_IDX_SHIFT) & GASNETI_TM_PAIR_IDX_MASK;
}
GASNETI_INLINE(gasneti_tm_pair_rem_idx)
gex_EP_Index_t gasneti_tm_pair_rem_idx(gasneti_TM_Pair_t _tm_pair)
{
gasneti_assertvalid_tm_pair(_tm_pair);
return (_tm_pair >> GASNETI_TM_PAIR_REM_IDX_SHIFT) & GASNETI_TM_PAIR_IDX_MASK;
}
// Given (tm,rank) return the jobrank or ep_location
extern GASNETI_PURE gex_Rank_t gasneti_tm_fwd_rank(gasneti_TM_t _tm, gex_Rank_t _rank);
GASNETI_PUREP(gasneti_tm_fwd_rank)
extern GASNETI_PURE gex_EP_Location_t gasneti_tm_fwd_location(gasneti_TM_t _tm, gex_Rank_t _rank, gex_Flags_t _flags);
GASNETI_PUREP(gasneti_tm_fwd_location)
// Given (tm,jobrank) return the rank of jobrank in tm, or GEX_RANK_INVALID
extern gex_Rank_t gasneti_tm_rev_rank(gasneti_TM_t _tm, gex_Rank_t _jobrank);
// Helpers which deal correctly/transparently with TM-pair
GASNETI_INLINE(gasneti_e_tm_size)
gex_Rank_t gasneti_e_tm_size(gex_TM_t _e_tm) {
gasneti_assert(_e_tm);
return gasneti_e_tm_is_pair(_e_tm) ? gex_System_QueryJobSize() : gex_TM_QuerySize(_e_tm);
}
GASNETI_INLINE(gasneti_i_tm_size)
gex_Rank_t gasneti_i_tm_size(gasneti_TM_t _i_tm) {
gasneti_assert(_i_tm);
return gasneti_i_tm_is_pair(_i_tm) ? gex_System_QueryJobSize() : _i_tm->_size;
}
GASNETI_INLINE(gasneti_i_tm_to_ep_index)
gex_EP_Index_t gasneti_i_tm_to_ep_index(gasneti_TM_t _i_tm) {
gasneti_assert(_i_tm);
if (gasneti_is_tm0(_i_tm)) {
return 0; // fast path
} else if (gasneti_i_tm_is_pair(_i_tm)) {
return gasneti_tm_pair_loc_idx(gasneti_i_tm_to_pair(_i_tm));
} else {
return _i_tm->_ep->_index;
}
}
#define gasneti_e_tm_to_ep_index(_e_tm) gasneti_i_tm_to_ep_index(gasneti_import_tm(_e_tm))
#if GASNET_DEBUG
GASNETI_INLINE(gasneti_check_jobrank)
void gasneti_check_jobrank(gex_Rank_t _jobrank) {
gasneti_assert_uint(_jobrank ,<, gex_System_QueryJobSize());
}
GASNETI_INLINE(gasneti_check_e_tm_rank)
void gasneti_check_e_tm_rank(gex_TM_t _e_tm, gex_Rank_t _rank) {
gasneti_assert(_e_tm);
gasneti_assert_uint(_rank ,<, gasneti_e_tm_size(_e_tm));
}
GASNETI_INLINE(gasneti_check_i_tm_rank)
void gasneti_check_i_tm_rank(gasneti_TM_t _i_tm, gex_Rank_t _rank) {
gasneti_assert(_i_tm);
gasneti_assert_uint(_rank ,<, gasneti_i_tm_size(_i_tm));
}
#else
#define gasneti_check_jobrank(jobrank) ((void)0)
#define gasneti_check_i_tm_rank(tm,rank) ((void)0)
#define gasneti_check_e_tm_rank(tm,rank) ((void)0)
#endif
// TODO-EX: remove when a runtime branch on tm->_rank_map is necessary
#define GASNETI_ALLOW_SPARSE_TEAMREP 0
GASNETI_INLINE(gasneti_i_tm_rank_to_jobrank)
gex_Rank_t gasneti_i_tm_rank_to_jobrank(gasneti_TM_t _i_tm, gex_Rank_t _rank) {
gasneti_check_i_tm_rank(_i_tm, _rank);
if (gasneti_is_tm0(_i_tm) || gasneti_i_tm_is_pair(_i_tm)) return _rank;
if (!GASNETI_ALLOW_SPARSE_TEAMREP || _i_tm->_rank_map) {
gasneti_assert(_i_tm->_rank_map);
return _i_tm->_rank_map[_rank];
}
return gasneti_tm_fwd_rank(_i_tm, _rank);
}
#define gasneti_e_tm_rank_to_jobrank(e_tm,rank) \
gasneti_i_tm_rank_to_jobrank(gasneti_import_tm(e_tm),rank)
GASNETI_INLINE(gasneti_i_tm_rank_to_ep_index)
gex_EP_Index_t gasneti_i_tm_rank_to_ep_index(gasneti_TM_t _i_tm, gex_Rank_t _rank) {
gasneti_check_i_tm_rank(_i_tm, _rank);
gex_EP_Index_t _result;
if (gasneti_is_tm0(_i_tm)) {
_result = 0;
} else if (gasneti_i_tm_is_pair(_i_tm)) {
_result = gasneti_tm_pair_rem_idx(gasneti_i_tm_to_pair(_i_tm));
} else if (!GASNETI_ALLOW_SPARSE_TEAMREP || _i_tm->_rank_map) {
// NULL _index_map indicates all members of TM are primordial EPs (idx==0)
_result = _i_tm->_index_map ? _i_tm->_index_map[_rank] : 0;
} else {
gex_EP_Location_t _loc = gasneti_tm_fwd_location(_i_tm, _rank, 0);
_result = _loc.gex_ep_index;
}
gasneti_assert(_result < GASNET_MAXEPS);
return _result;
}
#define gasneti_e_tm_rank_to_ep_index(e_tm,rank) \
gasneti_i_tm_rank_to_ep_index(gasneti_import_tm(e_tm),rank)
GASNETI_INLINE(gasneti_i_tm_rank_to_location)
gex_EP_Location_t gasneti_i_tm_rank_to_location(gasneti_TM_t _i_tm, gex_Rank_t _rank, gex_Flags_t _flags) {
gasneti_check_i_tm_rank(_i_tm, _rank);
gex_EP_Location_t _result;
#if (GASNET_MAXEPS == 1)
_result.gex_ep_index = 0;
#endif
if (gasneti_is_tm0(_i_tm)) {
_result.gex_rank = _rank;
#if (GASNET_MAXEPS > 1)
_result.gex_ep_index = 0;
#endif
} else if (gasneti_i_tm_is_pair(_i_tm)) {
_result.gex_rank = _rank;
#if (GASNET_MAXEPS > 1)
_result.gex_ep_index = gasneti_tm_pair_rem_idx(gasneti_i_tm_to_pair(_i_tm));
#endif
} else if (!GASNETI_ALLOW_SPARSE_TEAMREP || _i_tm->_rank_map) {
gasneti_assert(_i_tm->_rank_map);
_result.gex_rank = _i_tm->_rank_map[_rank];
#if (GASNET_MAXEPS > 1)
// NULL _index_map indicates all members of TM are primordial EPs (idx==0)
_result.gex_ep_index = _i_tm->_index_map ? _i_tm->_index_map[_rank] : 0;
#endif
} else {
_result = gasneti_tm_fwd_location(_i_tm, _rank, _flags);
}
return _result;
}
#define gasneti_e_tm_rank_to_location(e_tm,rank,flags) \
gasneti_i_tm_rank_to_location(gasneti_import_tm(e_tm),rank,flags)
GASNETI_INLINE(gasneti_i_tm_jobrank_to_rank)
gex_Rank_t gasneti_i_tm_jobrank_to_rank(gasneti_TM_t _i_tm, gex_Rank_t _jobrank) {
gasneti_assert(_i_tm);
gasneti_assert_uint(_jobrank ,<, gex_System_QueryJobSize());
if (gasneti_is_tm0(_i_tm) || gasneti_i_tm_is_pair(_i_tm)) return _jobrank;
return gasneti_tm_rev_rank(_i_tm, _jobrank);
}
#define gasneti_e_tm_jobrank_to_rank(e_tm,jobrank) \
gasneti_i_tm_jobrank_to_rank(gasneti_import_tm(e_tm),jobrank)
extern gasnet_seginfo_t *gasneti_seginfo;
extern gasnet_seginfo_t *gasneti_seginfo_aux;
extern gasnet_seginfo_t *gasneti_seginfo_tbl[GASNET_MAXEPS];
extern const gasnet_seginfo_t gasneti_null_segment;
// TODO: work towards dropping non-scalable seginfo tables
GASNETI_INLINE(gasneti_client_seginfo)
const gasnet_seginfo_t *gasneti_client_seginfo(gex_TM_t _e_tm, gex_Rank_t _rank) {
gex_EP_Location_t _loc = gasneti_e_tm_rank_to_location(_e_tm, _rank, 0);
gex_Rank_t _jobrank = _loc.gex_rank;
gex_EP_Index_t _idx = _loc.gex_ep_index;
gasnet_seginfo_t *_si_array = gasneti_seginfo_tbl[_idx];
if_pf (!_si_array) return &gasneti_null_segment;
return _si_array + _jobrank;
}
GASNETI_INLINE(gasneti_aux_seginfo)
const gasnet_seginfo_t *gasneti_aux_seginfo(gex_Rank_t _jobrank) {
return gasneti_seginfo_aux + _jobrank;
}
GASNETI_INLINE(_gasneti_in_seginfo_t)
int _gasneti_in_seginfo_t(const void *_ptr, size_t _nbytes, const gasnet_seginfo_t *_seginfo) {
gasneti_assert(_nbytes); // precondition to avoid "fence post" error at top of segment
uintptr_t _uptr = (uintptr_t)_ptr;
uintptr_t _segbase = (uintptr_t)_seginfo->addr;
return (_uptr >= _segbase && (_uptr + _nbytes) <= (_segbase + _seginfo->size));
}
GASNETI_INLINE(_gasneti_in_segment_t)
int _gasneti_in_segment_t(const void *_ptr, size_t _nbytes, const gex_Segment_t _segment) {
gasneti_assert(_nbytes); // precondition to avoid "fence post" error at top of segment
uintptr_t _uptr = (uintptr_t)_ptr;
gasneti_Segment_t _i_seg = gasneti_import_segment_valid(_segment);
return (_uptr >= (uintptr_t)(_i_seg->_addr) && (_uptr + _nbytes) <= (uintptr_t)_i_seg->_ub);
}
#if GASNET_SEGMENT_EVERYTHING
// Do not remove calls to gasneti_inseg_helper() or reduce it to a macro.
// The function call ensures ptr and nbytes evaluated exactly once for possible side-effects.
GASNETI_INLINE(gasneti_inseg_helper)
void gasneti_inseg_helper(const void *_ptr, size_t _nbytes) {
gasneti_assert(_nbytes);
gasneti_assert(_ptr);
}
#define gasneti_in_clientsegment(e_tm,rank,ptr,nbytes) \
(gasneti_inseg_helper(ptr,nbytes),gasneti_check_e_tm_rank(e_tm,rank), 1)
#define gasneti_in_auxsegment(jobrank,ptr,nbytes) \
(gasneti_inseg_helper(ptr,nbytes),gasneti_check_jobrank(jobrank), 1)
#define gasneti_in_fullsegment(e_tm,rank,ptr,nbytes) \
(gasneti_inseg_helper(ptr,nbytes), gasneti_check_e_tm_rank(e_tm,rank), 1)
#else
#define gasneti_in_clientsegment(e_tm,rank,ptr,nbytes) \
_gasneti_in_seginfo_t(ptr,nbytes,gasneti_client_seginfo(e_tm,rank))
#define gasneti_in_auxsegment(jobrank,ptr,nbytes) \
_gasneti_in_seginfo_t(ptr,nbytes,gasneti_aux_seginfo(jobrank))
GASNETI_INLINE(gasneti_in_fullsegment)
int gasneti_in_fullsegment(gex_TM_t _e_tm, gex_Rank_t _rank, const void *_ptr, size_t _nbytes) {
return gasneti_in_clientsegment(_e_tm, _rank, _ptr, _nbytes) ||
gasneti_in_auxsegment(gasneti_e_tm_rank_to_jobrank(_e_tm, _rank), _ptr, _nbytes);
}
#endif
// Local-only in-segment checks, taking a gasneti_EP_t to name the local endpoint
#if GASNET_SEGMENT_EVERYTHING
#define gasneti_in_local_auxsegment(ep,ptr,nbytes) 1
#define gasneti_in_local_clientsegment(ep,ptr,nbytes) 1
#define gasneti_in_local_fullsegment(ep,ptr,nbytes) 1
#else
GASNETI_INLINE(gasneti_in_local_auxsegment)
int gasneti_in_local_auxsegment(gasneti_EP_t _ep, void *_ptr, size_t _nbytes) {
// TODO-EX: needs update for multi-{EP,Segment}
return _gasneti_in_seginfo_t(_ptr, _nbytes, &gasneti_seginfo_aux[gasneti_mynode]);
}
GASNETI_INLINE(gasneti_in_local_clientsegment)
int gasneti_in_local_clientsegment(gasneti_EP_t _ep, void *_ptr, size_t _nbytes) {
gex_Segment_t _segment = gasneti_export_segment(_ep->_segment);
return _segment && _gasneti_in_segment_t(_ptr, _nbytes, _segment);
}
GASNETI_INLINE(gasneti_in_local_fullsegment)
int gasneti_in_local_fullsegment(gasneti_EP_t _ep, void *_ptr, size_t _nbytes) {
return gasneti_in_local_clientsegment(_ep, _ptr, _nbytes) ||
gasneti_in_local_auxsegment(_ep, _ptr, _nbytes);
}
#endif
#ifdef _INCLUDED_GASNET_INTERNAL_H
/* default for GASNet implementation is to check against union of client and aux segments */
#define gasneti_in_segment gasneti_in_fullsegment
#define gasneti_in_local_segment gasneti_in_local_fullsegment
#else
/* default for client is to check against just the client seg */
#define gasneti_in_segment gasneti_in_clientsegment
#define gasneti_in_local_segment gasneti_in_local_clientsegment
#endif
#define _gasneti_boundscheck(e_tm,rank,ptr,nbytes,segtest) do { \
gex_TM_t _gex_bc_tm = (e_tm); \
gasneti_assert(_gex_bc_tm); \
gex_Rank_t _gex_bc_rank = (rank); \
gex_Rank_t _gex_bc_size = gasneti_e_tm_size(_gex_bc_tm); \
const void *_gex_bc_ptr = (const void *)(ptr); \
size_t _gex_bc_nbytes = (size_t)(nbytes); \
gasneti_assert(_gex_bc_nbytes); /* avoids "fence post" error */ \
if_pf (_gex_bc_rank >= _gex_bc_size) \
gasneti_fatalerror("Rank out of range (%lu >= %lu)", \
(unsigned long)_gex_bc_rank, (unsigned long)(_gex_bc_size)); \
if_pf (_gex_bc_ptr == NULL || \
!segtest(_gex_bc_tm,_gex_bc_rank,_gex_bc_ptr,_gex_bc_nbytes)) { \
const gasnet_seginfo_t *_gex_bc_client_seg = \
gasneti_client_seginfo(_gex_bc_tm,_gex_bc_rank); \
gex_Rank_t _gex_bc_jobrank = gasneti_e_tm_rank_to_jobrank(_gex_bc_tm, _gex_bc_rank); \
const gasnet_seginfo_t *_gex_bc_aux_seg = \
gasneti_aux_seginfo(_gex_bc_jobrank); \
gasneti_fatalerror("Remote address out of range (" GASNETI_TMRANKFMT \
" ptr=" GASNETI_LADDRFMT" nbytes=%" PRIuPTR ")" \
"\n clientsegment=(" GASNETI_LADDRFMT"..." GASNETI_LADDRFMT")" \
"\n auxsegment=(" GASNETI_LADDRFMT"..." GASNETI_LADDRFMT")", \
GASNETI_TMRANKSTR(_gex_bc_tm,_gex_bc_rank), \
GASNETI_LADDRSTR(_gex_bc_ptr), \
(uintptr_t)_gex_bc_nbytes, \
GASNETI_LADDRSTR(_gex_bc_client_seg->addr), \
GASNETI_LADDRSTR((uintptr_t)_gex_bc_client_seg->addr + \
_gex_bc_client_seg->size), \
GASNETI_LADDRSTR(_gex_bc_aux_seg->addr), \
GASNETI_LADDRSTR((uintptr_t)_gex_bc_aux_seg->addr + \
_gex_bc_aux_seg->size) \
); \
} \
} while(0)
// gasneti_boundscheck()
#if GASNET_NDEBUG
#define gasneti_boundscheck(e_tm,rank,ptr,nbytes) ((void)0)
#else
#define gasneti_boundscheck(e_tm,rank,ptr,nbytes) \
_gasneti_boundscheck(e_tm,rank,ptr,nbytes,gasneti_in_segment)
#endif
/* make a GASNet core API call - if it fails, print error message and abort */
#ifndef GASNETI_SAFE
#define GASNETI_SAFE(fncall) do { \
int _retcode = (fncall); \
if_pf (_retcode != (int)GASNET_OK) { \
gasneti_fatalerror("\nGASNet encountered an error: %s(%i)\n" \
" while calling: %s", \
gasnet_ErrorName(_retcode), _retcode, #fncall); \
} \
} while (0)
#endif
/* ------------------------------------------------------------------------------------ */
/* tools for dealing with gex_Event_t* pre-defined values */
// TODO-EX: move to gasnet_event_internal.h
#ifndef _GEX_EVENT_T
GASNETI_INLINE(gasneti_leaf_is_pointer) GASNETI_PURE
int gasneti_leaf_is_pointer(const gex_Event_t *_opt_val) {
gasneti_assert(_opt_val != NULL);
return ((uintptr_t)(_opt_val) >= (uintptr_t)4);
}
GASNETI_PUREP(gasneti_leaf_is_pointer)
GASNETI_INLINE(gasneti_leaf_finish)
void gasneti_leaf_finish(gex_Event_t *_opt_val) {
if (gasneti_leaf_is_pointer(_opt_val)) *_opt_val = GEX_EVENT_INVALID;
}
#if (PLATFORM_COMPILER_INTEL && PLATFORM_COMPILER_VERSION_LT(19,0,20180800))
// Some Intel C prior to 2019.0.117 (builddate 20180804) issue a buggy warning
// about side effects in an __assume(), and these versions predate Intel's
// support for __builtin_assume, which avoids the warning.
#define gasneti_assume_leaf_is_pointer(lc_opt) do { \
GASNETI_PRAGMA(warning push); \
GASNETI_PRAGMA(warning disable 2261); \
gasneti_assume(gasneti_leaf_is_pointer(lc_opt)); \
GASNETI_PRAGMA(warning pop); \
} while (0)
#else
#define gasneti_assume_leaf_is_pointer(lc_opt) \
gasneti_assume(gasneti_leaf_is_pointer(lc_opt))
#endif
#endif // _GEX_EVENT_T
/* ------------------------------------------------------------------------------------ */
/* semi-portable spinlocks using gasneti_atomic_t
This useful primitive is not available on all platforms and it therefore reserved
for internal use only.
On platforms where implemented, the following are roughly equivalent to the
corresponding pthread_mutex_* calls:
GASNETI_SPINLOCK_INITIALIZER
gasneti_spinlock_{init,destroy,lock,unlock,trylock}
The functions return 0 on success to match the corresponding pthread_mutex functions.
There is no gasneti_spinlock_t, these functions operate on gasneti_atomic_t.
Unlike the pthread_mutex, the use of spinlocks have no fairness guarantees. For
instance, it would be perfectly legal for a race to always grant the lock to the CPU
which "owns" the associated memory. Therefore, spinlocks must be used with care.
Also unlike pthread_mutex, it is safe to unlock one from signal context. Though
trying to acquire a spinlock in signal context is legal, it is dangerous.
Like the pthread_mutex, these operations perform an RMB() when acquiring the
lock and a WMB() when releasing it. So, no additional memory barriers are
required when using these locks to protect one's data structures.
GASNETI_HAVE_SPINLOCK will be defined to 1 on platforms supporting this primitive.
*/
#if 0
/* TODO Some platforms may have cheaper implementations than atomic-CAS. */
/* eg. some platforms (SPARC?) can support spinlock using test-and-set */
#elif defined(GASNETI_ATOMICOPS_NOT_SIGNALSAFE)
/* We don't implement this case due to lack of signal safety */
#elif defined(GASNETI_HAVE_ATOMIC_CAS)
#if GASNET_DEBUG
#define GASNETI_SPINLOCK_LOCKED 0xa5a5
#define GASNETI_SPINLOCK_UNLOCKED 0xaa55
#define GASNETI_SPINLOCK_DESTROYED 0xDEAD
GASNETI_INLINE(gasneti_spinlock_is_valid)
int gasneti_spinlock_is_valid(gasneti_atomic_t *_plock) {
uint32_t _tmp = gasneti_atomic_read(_plock, GASNETI_ATOMIC_RMB_PRE);
if_pf (_tmp == GASNETI_SPINLOCK_DESTROYED)
gasneti_fatalerror("Detected use of destroyed spinlock");
if_pf (!((_tmp == GASNETI_SPINLOCK_LOCKED) || (_tmp == GASNETI_SPINLOCK_UNLOCKED)))
gasneti_fatalerror("Detected use of uninitialized or corrupted spinlock");
return 1;
}
GASNETI_INLINE(gasneti_spinlock_is_locked)
int gasneti_spinlock_is_locked(gasneti_atomic_t *_plock) {
uint32_t _tmp = gasneti_atomic_read(_plock, GASNETI_ATOMIC_RMB_PRE);
return (_tmp == GASNETI_SPINLOCK_LOCKED);
}
#else
#define GASNETI_SPINLOCK_LOCKED 1
#define GASNETI_SPINLOCK_UNLOCKED 0
#define gasneti_spinlock_is_valid(plock) 1
#endif
#define GASNETI_SPINLOCK_INITIALIZER gasneti_atomic_init(GASNETI_SPINLOCK_UNLOCKED)
#define gasneti_spinlock_init(plock) \
gasneti_atomic_set((plock), GASNETI_SPINLOCK_UNLOCKED, GASNETI_ATOMIC_WMB_POST)
#define gasneti_spinlock_destroy(plock) \
gasneti_assert(gasneti_atomic_compare_and_swap(plock, GASNETI_SPINLOCK_UNLOCKED, GASNETI_SPINLOCK_DESTROYED, GASNETI_ATOMIC_WMB_POST))
#define gasneti_spinlock_lock(plock) do { \
gasneti_waituntil( \
!gasneti_spinlock_is_valid(plock) || \
gasneti_atomic_compare_and_swap(plock, \
GASNETI_SPINLOCK_UNLOCKED, GASNETI_SPINLOCK_LOCKED, 0) \
); /* Acquire: the rmb() is in the gasneti_waituntil() */ \
gasneti_assert(gasneti_spinlock_is_locked(plock)); \
} while (0)
GASNETI_INLINE(gasneti_spinlock_unlock)
int gasneti_spinlock_unlock(gasneti_atomic_t *_plock) {
#if GASNET_DEBUG
/* Using CAS for release is more costly, but adds validation */
gasneti_assert(gasneti_atomic_compare_and_swap(_plock, GASNETI_SPINLOCK_LOCKED, GASNETI_SPINLOCK_UNLOCKED, GASNETI_ATOMIC_REL));
#else
gasneti_atomic_set(_plock, GASNETI_SPINLOCK_UNLOCKED, GASNETI_ATOMIC_REL);
#endif
return 0;
}
/* return 0/EBUSY on success/failure to match pthreads */
GASNETI_INLINE(gasneti_spinlock_trylock) GASNETI_WARN_UNUSED_RESULT
int gasneti_spinlock_trylock(gasneti_atomic_t *_plock) {
gasneti_assert(gasneti_spinlock_is_valid(_plock));
if ((GASNETI_SPINLOCK_UNLOCKED == gasneti_atomic_read(_plock, 0)) &&
gasneti_atomic_compare_and_swap(_plock, GASNETI_SPINLOCK_UNLOCKED, GASNETI_SPINLOCK_LOCKED, GASNETI_ATOMIC_ACQ_IF_TRUE)) {
gasneti_assert(gasneti_spinlock_is_locked(_plock));
return 0;
} else {
return EBUSY;
}
}
#define GASNETI_HAVE_SPINLOCK 1
#else
/* Here we use a binary semaphore */
#define GASNETI_SPINLOCK_UNLOCKED 1
#define GASNETI_SPINLOCK_DESTROYED 2
#if GASNET_DEBUG
GASNETI_INLINE(gasneti_spinlock_is_valid)
int gasneti_spinlock_is_valid(gasneti_atomic_t *_plock) {
uint32_t _tmp = gasneti_atomic_read(_plock, GASNETI_ATOMIC_RMB_PRE);
if_pf (_tmp == GASNETI_SPINLOCK_DESTROYED)
gasneti_fatalerror("Detected use of destroyed spinlock");
return 1;
}
GASNETI_INLINE(gasneti_spinlock_is_locked)
int gasneti_spinlock_is_locked(gasneti_atomic_t *_plock) {
gasneti_atomic_val_t _tmp = gasneti_atomic_read(_plock, GASNETI_ATOMIC_RMB_PRE);
return (_tmp != GASNETI_SPINLOCK_UNLOCKED);
}
#else
#define gasneti_spinlock_is_valid(plock) 1
#endif
#define GASNETI_SPINLOCK_INITIALIZER gasneti_atomic_init(GASNETI_SPINLOCK_UNLOCKED)
#define gasneti_spinlock_init(plock) \
gasneti_atomic_set((plock), GASNETI_SPINLOCK_UNLOCKED, GASNETI_ATOMIC_WMB_POST)
#define gasneti_spinlock_destroy(plock) do { \
gasneti_assert(!gasneti_spinlock_is_locked(plock)); \
gasneti_atomic_set((plock), GASNETI_SPINLOCK_DESTROYED, GASNETI_ATOMIC_WMB_POST); \
} while (0)
GASNETI_INLINE(_gasneti_spinlock_try) GASNETI_WARN_UNUSED_RESULT
int _gasneti_spinlock_try(gasneti_atomic_t *_plock) {
gasneti_assert(gasneti_spinlock_is_valid(_plock));
return (gasneti_atomic_read(_plock, 0) == GASNETI_SPINLOCK_UNLOCKED) &&
gasneti_atomic_decrement_and_test(_plock, GASNETI_ATOMIC_ACQ_IF_TRUE);
}
/* Ick: forward reference to GASNETI_WAITHOOK only works because this is a macro */
#define gasneti_spinlock_lock(plock) do { \
while (!_gasneti_spinlock_try(plock)) { \
GASNETI_WAITHOOK(); \
} \
} while (0)
GASNETI_INLINE(gasneti_spinlock_unlock)
int gasneti_spinlock_unlock(gasneti_atomic_t *_plock) {
gasneti_assert(gasneti_spinlock_is_locked(_plock));
gasneti_atomic_set(_plock, GASNETI_SPINLOCK_UNLOCKED, GASNETI_ATOMIC_REL);
return 0;
}
/* return 0/EBUSY on success/failure to match pthreads */
GASNETI_INLINE(gasneti_spinlock_trylock) GASNETI_WARN_UNUSED_RESULT
int gasneti_spinlock_trylock(gasneti_atomic_t *_plock) {
return _gasneti_spinlock_try(_plock) ? 0 : EBUSY;
}
#define GASNETI_HAVE_SPINLOCK 1
#endif
/* ------------------------------------------------------------------------------------ */
/* public threadinfo support */
#if GASNETI_CLIENT_THREADS
#if defined GASNETI_THREADINFO_OPT_CONFIGURE
/* take user-specified value, if any*/
#undef GASNETI_THREADINFO_OPT
#define GASNETI_THREADINFO_OPT GASNETI_THREADINFO_OPT_CONFIGURE
#elif defined GASNETI_THREADINFO_OPT
/* keep conduit-specified value */
#elif defined GASNETI_HAVE_TLS_SUPPORT
/* Default to OFF on some common ABIs with good TLS support */
#if (PLATFORM_ARCH_X86_64 || PLATFORM_ARCH_MIC) && \
(PLATFORM_OS_DARWIN || PLATFORM_OS_SOLARIS || PLATFORM_OS_LINUX)
#define GASNETI_THREADINFO_OPT 0
#elif PLATFORM_ARCH_POWERPC && \
PLATFORM_OS_LINUX
#define GASNETI_THREADINFO_OPT 0
#elif PLATFORM_ARCH_AARCH64 && \
(PLATFORM_OS_LINUX || PLATFORM_OS_DARWIN)
#define GASNETI_THREADINFO_OPT 0
#endif
#endif
#ifndef GASNETI_THREADINFO_OPT
/* Default to ON if not set anywhere above */
#define GASNETI_THREADINFO_OPT 1
#endif
#ifndef GASNETI_LAZY_BEGINFUNCTION
#define GASNETI_LAZY_BEGINFUNCTION 1
#endif
#endif
#if GASNETI_THREADINFO_OPT
/* Here we use a clever trick - GASNET_GET_THREADINFO() uses the sizeof(_gasneti_threadinfo_available)
to determine whether gasneti_threadinfo_cache was bound a value posted by GASNET_POST_THREADINFO()
of if it bound to the globally declared dummy variables.
Even a very stupid C optimizer should constant-fold away the unused calls to gasneti_get_threadinfo()
and discard the unused variables
We need 2 separate variables to ensure correct name-binding semantics for GASNET_POST_THREADINFO(GASNET_GET_THREADINFO())
*/
#if PLATFORM_COMPILER_PGI_CXX
// Add a redundant value use to avoid a 550 set-but-not-used warning
// Not needed with NVHPC-branded releases
#define _GASNETI_THREAD_POSTED (sizeof(_gasneti_threadinfo_available) > 1 \
&& !_gasneti_threadinfo_available)
#else
#define _GASNETI_THREAD_POSTED (sizeof(_gasneti_threadinfo_available) > 1)
#endif
static gasnet_threadinfo_t _gasneti_threadinfo_cache = 0;
static uint8_t _gasneti_threadinfo_available =
sizeof(_gasneti_threadinfo_cache) + sizeof(_gasneti_threadinfo_available);
/* silly little trick to prevent unused variable warning on gcc -Wall */
// tmp variable below solves scoping problems on cache for expressions like:
// GASNET_POST_THREADINFO(GASNET_GET_THREADINFO())
// where the cache from an enclosing scope is consulted by that GET
#define GASNET_POST_THREADINFO(info) \
gasnet_threadinfo_t const _gasneti_threadinfo_tmp = (info); \
gasnet_threadinfo_t _gasneti_threadinfo_cache = _gasneti_threadinfo_tmp; \
uint32_t _gasneti_threadinfo_available = 0
/* if you get an unused variable warning on _gasneti_threadinfo_available,
it means you POST'ed in a function which made no GASNet calls that needed it
So, PLEASE don't add __unused__ annotations here. */
#if GASNETI_LAZY_BEGINFUNCTION
// bug 3498: Ensure a sequence point after the assignment to _gasneti_threadinfo_cache
GASNETI_INLINE(gasneti_lazy_get_threadinfo) GASNETI_WARN_UNUSED_RESULT
gasnet_threadinfo_t gasneti_lazy_get_threadinfo(gasnet_threadinfo_t * const _p_ti,
gasnet_threadinfo_t _ti_val) {
return (*_p_ti = _ti_val);
}
// memoized implementation of GET POSTED, where BEGIN_FUNCTION delays lookup to first use
#define _GASNETI_GET_THREADINFO_POSTED() \
( GASNETT_PREDICT_TRUE(_gasneti_threadinfo_cache) ? _gasneti_threadinfo_cache : \
gasneti_lazy_get_threadinfo(&_gasneti_threadinfo_cache,(gasnet_threadinfo_t)_gasneti_mythread_slow()) \
)
#else
// straightforward implementation of GET POSTED
#define _GASNETI_GET_THREADINFO_POSTED() ( _gasneti_threadinfo_cache )
#endif
// GASNET_GET_THREADINFO: the main cached-lookup workhorse
// note both the branches textually below are always statically resolved
#define GASNET_GET_THREADINFO() \
( _GASNETI_THREAD_POSTED ? /* have POST or BEGIN_FUNCTION ? */ \
_GASNETI_GET_THREADINFO_POSTED() /* use it */ : \
( _GASNETI_THREAD_FARG_AVAILABLE ? /* in FARG context? use. */ \
(gasnet_threadinfo_t)(uintptr_t)_GASNETI_THREAD_FARG_NAME : \
(gasnet_threadinfo_t)_gasneti_mythread_slow() /* lookup */ ) \
)
/* the gasnet_threadinfo_t pointer points to a thread data-structure owned by
the extended API, whose first element is a pointer reserved
for use by the core API (initialized to NULL)
*/
#if GASNETI_LAZY_BEGINFUNCTION
/* postpone thread discovery to first use */
#define GASNET_BEGIN_FUNCTION() GASNET_POST_THREADINFO(0)
#else
#define GASNET_BEGIN_FUNCTION() GASNET_POST_THREADINFO(GASNET_GET_THREADINFO())
#endif
#else
#if GASNET_DEBUG
#define GASNET_POST_THREADINFO(info) \
static void *_gasneti_threadinfo_dummy = /* diagnose duplicate POST in a scope */ \
(void *)&_gasneti_threadinfo_dummy;
#else
#define GASNET_POST_THREADINFO(info) ((void)0)
#endif
#define GASNET_GET_THREADINFO() (NULL)
#define GASNET_BEGIN_FUNCTION() GASNET_POST_THREADINFO(GASNET_GET_THREADINFO())
#endif
/* ------------------------------------------------------------------------------------ */
/* thread-id optimization support */
#if GASNETI_THREADINFO_OPT
#if GASNETI_RESTRICT_MAY_QUALIFY_TYPEDEFS
#define _GASNETI_THREAD_FARG_RTYPE gasnet_threadinfo_t
#else
#define _GASNETI_THREAD_FARG_RTYPE void *
#endif
#define _GASNETI_THREAD_FARG_TYPE _GASNETI_THREAD_FARG_RTYPE const GASNETI_RESTRICT
#define _GASNETI_THREAD_FARG_NAME _gasneti_threadinfo_farg
static uint8_t _GASNETI_THREAD_FARG_NAME = sizeof(_GASNETI_THREAD_FARG_NAME);
#define _GASNETI_THREAD_FARG_AVAILABLE (sizeof(_GASNETI_THREAD_FARG_NAME) > 1)
// -----------------------------------------------------------------------------------------
// *** Propagating info into GASNETI_THREAD_FARG function context ***
// GASNETI_THREAD_FARG(_ALONE): use to declare the threadinfo hidden arg as part of a function declaration
#define GASNETI_THREAD_FARG , GASNETI_THREAD_FARG_ALONE
#define GASNETI_THREAD_FARG_ALONE _GASNETI_THREAD_FARG_TYPE _GASNETI_THREAD_FARG_NAME
// GASNETI_THREAD_GET(_ALONE): retrieve the threadinfo (when GASNETI_THREADINFO_OPT) from one of:
// a prior GASNET_POST_THREADINFO, an FARG to the enclosing function, or dynamic lookup
// and pass as the hidden argument to a function declared using GASNETI_THREAD_FARG(_ALONE)
#define GASNETI_THREAD_GET , GASNETI_THREAD_GET_ALONE
#define GASNETI_THREAD_GET_ALONE GASNET_GET_THREADINFO()
// -----------------------------------------------------------------------------------------
// *** Inside FARG/POST'd context ***
//
// The macros in this section should ONLY be used by code inside an FARG/POST'd context, ie:
// 1. inside functions declared using GASNETI_THREAD_FARG*.
// 2. within the lexical scope of a GASNET_BEGIN_FUNCTION() or GASNET_POST_THREADINFO()
// Otherwise, they generate a compiler diagnostic in GASNETI_THREADINFO_OPT mode.
// the error looks something like: "error: size of array is negative"
// This is helpful in contexts where we wish to statically ensure lack of dynamic lookup.
// GASNETI_THREAD_PASS(_ALONE): propagate the threadinfo to a callee declared with GASNETI_THREAD_FARG*
// this only differs from GASNETI_THREAD_GET* in that it statically requires FARG/POST'd context (and no lookup)
#define GASNETI_THREAD_PASS , GASNETI_THREAD_PASS_ALONE
#define GASNETI_THREAD_PASS_ALONE ( gasneti_static_assert(_GASNETI_THREAD_FARG_AVAILABLE||_GASNETI_THREAD_POSTED), \
( _GASNETI_THREAD_POSTED ? \
(_GASNETI_THREAD_FARG_RTYPE)_GASNETI_GET_THREADINFO_POSTED() : \
(_GASNETI_THREAD_FARG_RTYPE)(uintptr_t)_GASNETI_THREAD_FARG_NAME ) )
// GASNETI_MYTHREAD: retrieve the value of the threadinfo as a (gasneti_threaddata_t *),
// suitable for access to threaddata fields from internal code
#define GASNETI_MYTHREAD ( gasneti_static_assert(_GASNETI_THREAD_FARG_AVAILABLE||_GASNETI_THREAD_POSTED), \
(_GASNETI_THREAD_POSTED ? \
(struct _gasneti_threaddata_t *)_GASNETI_GET_THREADINFO_POSTED() : \
(struct _gasneti_threaddata_t *)(uintptr_t)_GASNETI_THREAD_FARG_NAME ) )
// -----------------------------------------------------------------------------------------
// *** Declaring GASNETI_THREAD_FARG context : NO LONGER SUPPORTED ***
// Former macros:
// GASNETI_THREAD_LOOKUP has been superceded by GASNET_BEGIN_FUNCTION
// GASNETI_THREAD_POST() has been superceded by GASNET_POST_THREADINFO()
// GASNETI_THREAD_GET* now automatically pulls from these sources or FARG before lookup.
// -----------------------------------------------------------------------------------------
// *** Other ***
// GASNETI_MYTHREAD_GET_OR_LOOKUP: force retrieve my (gasneti_threaddata_t *) from one of:
// a prior GASNET_POST_THREADINFO, an FARG to the enclosing function, or dynamic lookup
// This is essentially GASNETI_MYTHREAD without requiring FARG/POST'd context (allows lookup)
// Only valid known use is macros that expand threaddata field access directly into an
// "unknown" context, such as in client code or certain cases of internal code with callers
// in multiple conduits and/or subsystems.
// This is NOT suitable for internal code in which the macro definition and its callers fall
// within a single conduit or subsystem. Such cases should instead establish FARG/POST'd
// context and use GASNETI_MYTHREAD.
#define GASNETI_MYTHREAD_GET_OR_LOOKUP ((struct _gasneti_threaddata_t *)GASNET_GET_THREADINFO())
#else
#define GASNETI_THREAD_FARG
#define GASNETI_THREAD_FARG_ALONE void
#define GASNETI_THREAD_GET
#define GASNETI_THREAD_GET_ALONE
#define GASNETI_THREAD_PASS
#define GASNETI_THREAD_PASS_ALONE
#define GASNETI_MYTHREAD (_gasneti_mythread_slow())
#define GASNETI_MYTHREAD_GET_OR_LOOKUP GASNETI_MYTHREAD
#endif
// Misc thread-id related goop
// GASNETI_THREAD_SWALLOW: Utility to discard an FARG passed to a 0-arg function-like macro
// TODO-EX: move and rename this essentially unrelated utility macro
#define GASNETI_THREAD_SWALLOW(x)
/* ------------------------------------------------------------------------------------ */
/* GASNETI_MAX_THREADS: cannot exceed the size representable in gasnete_threadidx_t,
but some conduits or configures may set it to less */
#if GASNET_SEQ /* only one client thread by definition */
#undef GASNETI_MAX_THREADS
#ifdef GASNETE_CONDUIT_THREADS_USING_TD
#define GASNETI_MAX_THREADS (1 + GASNETE_CONDUIT_THREADS_USING_TD)
#else
#define GASNETI_MAX_THREADS 1
#endif
#define GASNETI_MAX_THREADS_REASON "GASNET_SEQ mode only supports single-threaded operation."
#elif defined(GASNETI_MAX_THREADS) /* conduit-imposed limit */
#if defined(GASNETI_MAX_THREADS_CONFIGURE) && GASNETI_MAX_THREADS_CONFIGURE < GASNETI_MAX_THREADS
#undef GASNETI_MAX_THREADS /* limit lowered by configure */
#define GASNETI_MAX_THREADS GASNETI_MAX_THREADS_CONFIGURE
#else
#define GASNETI_MAX_THREADS_REASON "This limit is imposed by " GASNET_EXTENDED_NAME_STR " conduit."
#endif
#else /* default */
#if GASNETI_MAX_THREADS_CONFIGURE
#define GASNETI_MAX_THREADS GASNETI_MAX_THREADS_CONFIGURE
#else /* default */
#define GASNETI_MAX_THREADS 256
#endif
#endif