1 //------------------------------------------------------------------------------
2 // GB_context.h: definitions for the internal context
3 //------------------------------------------------------------------------------
4 
5 // SuiteSparse:GraphBLAS, Timothy A. Davis, (c) 2017-2021, All Rights Reserved.
6 // SPDX-License-Identifier: Apache-2.0
7 
8 //------------------------------------------------------------------------------
9 
10 #ifndef GB_CONTEXT_H
11 #define GB_CONTEXT_H
12 
13 //------------------------------------------------------------------------------
14 // GB_context: error logging, thread control, and Werk space
15 //------------------------------------------------------------------------------
16 
17 // Error messages are logged in Context->logger_handle, on the stack which is
18 // handle to the input/output matrix/vector (typically C).  If the user-defined
19 // data types, operators, etc have really long names, the error messages are
20 // safely truncated (via snprintf).  This is intentional, but gcc with
21 // -Wformat-truncation will print a warning (see pragmas above).  Ignore the
22 // warning.
23 
24 // The Context also contains the number of threads to use in the operation.  It
25 // is normally determined from the user's descriptor, with a default of
26 // nthreads_max = GxB_DEFAULT (that is, zero).  The default rule is to let
27 // GraphBLAS determine the number of threads automatically by selecting a
28 // number of threads between 1 and nthreads_max.  GrB_init initializes
29 // nthreads_max to omp_get_max_threads.  Both the global value and the value in
30 // a descriptor can set/queried by GxB_set / GxB_get.
31 
32 // Some GrB_Matrix and GrB_Vector methods do not take a descriptor, however
33 // (GrB_*_dup, _build, _exportTuples, _clear, _nvals, _wait, and GxB_*_resize).
34 // For those methods the default rule is always used (nthreads_max =
35 // GxB_DEFAULT), which then relies on the global nthreads_max.
36 
37 // GB_WERK_SIZE is the size of a small fixed-sized array in the Context, used
38 // for small werkspace allocations (typically O(# of threads or # tasks)).
39 // GB_WERK_SIZE must be a multiple of 8.  The Werk array is placed first in the
40 // GB_Context struct, to ensure proper alignment.
41 
42 #define GB_WERK_SIZE 16384
43 
44 typedef struct
45 {
46     GB_void Werk [GB_WERK_SIZE] ;   // werkspace stack
47     double chunk ;                  // chunk size for small problems
48     const char *where ;             // GraphBLAS function where error occurred
49     char **logger_handle ;          // error report
50     size_t *logger_size_handle ;
51     int nthreads_max ;              // max # of threads to use
52     int pwerk ;                     // top of Werk stack, initially zero
53 }
54 GB_Context_struct ;
55 
56 typedef GB_Context_struct *GB_Context ;
57 
58 // GB_WHERE keeps track of the currently running user-callable function.
59 // User-callable functions in this implementation are written so that they do
60 // not call other unrelated user-callable functions (except for GrB_*free).
61 // Related user-callable functions can call each other since they all report
62 // the same type-generic name.  Internal functions can be called by many
63 // different user-callable functions, directly or indirectly.  It would not be
64 // helpful to report the name of an internal function that flagged an error
65 // condition.  Thus, each time a user-callable function is entered, it logs the
66 // name of the function with the GB_WHERE macro.
67 
68 #define GB_CONTEXT(where_string)                                    \
69     /* construct the Context */                                     \
70     GB_Context_struct Context_struct ;                              \
71     GB_Context Context = &Context_struct ;                          \
72     /* set Context->where so GrB_error can report it if needed */   \
73     Context->where = where_string ;                                 \
74     /* get the default max # of threads and default chunk size */   \
75     Context->nthreads_max = GB_Global_nthreads_max_get ( ) ;        \
76     Context->chunk = GB_Global_chunk_get ( ) ;                      \
77     /* get the pointer to where any error will be logged */         \
78     Context->logger_handle = NULL ;                                 \
79     Context->logger_size_handle = NULL ;                            \
80     /* initialize the Werk stack */                                 \
81     Context->pwerk = 0 ;
82 
83 // C is a matrix, vector, scalar, or descriptor
84 #define GB_WHERE(C,where_string)                                    \
85     if (!GB_Global_GrB_init_called_get ( ))                         \
86     {                                                               \
87         return (GrB_PANIC) ; /* GrB_init not called */              \
88     }                                                               \
89     GB_CONTEXT (where_string)                                       \
90     if (C != NULL)                                                  \
91     {                                                               \
92         /* free any prior error logged in the object */             \
93         GB_FREE (&(C->logger), C->logger_size) ;                    \
94         Context->logger_handle = &(C->logger) ;                     \
95         Context->logger_size_handle = &(C->logger_size) ;           \
96     }
97 
98 // create the Context, with no error logging
99 #define GB_WHERE1(where_string)                                     \
100     if (!GB_Global_GrB_init_called_get ( ))                         \
101     {                                                               \
102         return (GrB_PANIC) ; /* GrB_init not called */              \
103     }                                                               \
104     GB_CONTEXT (where_string)
105 
106 //------------------------------------------------------------------------------
107 // GB_GET_NTHREADS_MAX:  determine max # of threads for OpenMP parallelism.
108 //------------------------------------------------------------------------------
109 
110 //      GB_GET_NTHREADS_MAX obtains the max # of threads to use and the chunk
111 //      size from the Context.  If Context is NULL then a single thread *must*
112 //      be used.  If Context->nthreads_max is <= GxB_DEFAULT, then select
113 //      automatically: between 1 and nthreads_max, depending on the problem
114 //      size.  Below is the default rule.  Any function can use its own rule
115 //      instead, based on Context, chunk, nthreads_max, and the problem size.
116 //      No rule can exceed nthreads_max.
117 
118 #define GB_GET_NTHREADS_MAX(nthreads_max,chunk,Context)                     \
119     int nthreads_max = (Context == NULL) ? 1 : Context->nthreads_max ;      \
120     if (nthreads_max <= GxB_DEFAULT)                                        \
121     {                                                                       \
122         nthreads_max = GB_Global_nthreads_max_get ( ) ;                     \
123     }                                                                       \
124     double chunk = (Context == NULL) ? GxB_DEFAULT : Context->chunk ;       \
125     if (chunk <= GxB_DEFAULT)                                               \
126     {                                                                       \
127         chunk = GB_Global_chunk_get ( ) ;                                   \
128     }
129 
130 //------------------------------------------------------------------------------
131 // GB_nthreads: determine # of threads to use for a parallel loop or region
132 //------------------------------------------------------------------------------
133 
134 // If work < 2*chunk, then only one thread is used.
135 // else if work < 3*chunk, then two threads are used, and so on.
136 
GB_nthreads(double work,double chunk,int nthreads_max)137 static inline int GB_nthreads   // return # of threads to use
138 (
139     double work,                // total work to do
140     double chunk,               // give each thread at least this much work
141     int nthreads_max            // max # of threads to use
142 )
143 {
144     work  = GB_IMAX (work, 1) ;
145     chunk = GB_IMAX (chunk, 1) ;
146     int64_t nthreads = (int64_t) floor (work / chunk) ;
147     nthreads = GB_IMIN (nthreads, nthreads_max) ;
148     nthreads = GB_IMAX (nthreads, 1) ;
149     return ((int) nthreads) ;
150 }
151 
152 //------------------------------------------------------------------------------
153 // error logging
154 //------------------------------------------------------------------------------
155 
156 // The GB_ERROR macro logs an error in the logger error string.
157 //
158 //  if (i >= nrows)
159 //  {
160 //      GB_ERROR (GrB_INDEX_OUT_OF_BOUNDS,
161 //          "Row index %d out of bounds; must be < %d", i, nrows) ;
162 //  }
163 //
164 // The user can then do:
165 //
166 //  const char *error ;
167 //  GrB_error (&error, A) ;
168 //  printf ("%s", error) ;
169 
170 GB_PUBLIC   // accessed by the MATLAB tests in GraphBLAS/Test only
171 const char *GB_status_code (GrB_Info info) ;
172 
173 // maximum size of the error logger string
174 #define GB_LOGGER_LEN 384
175 
176 // log an error in the error logger string and return the error
177 #define GB_ERROR(info,format,...)                                           \
178 {                                                                           \
179     if (Context != NULL)                                                    \
180     {                                                                       \
181         char **logger_handle = Context->logger_handle ;                     \
182         if (logger_handle != NULL)                                          \
183         {                                                                   \
184             size_t *logger_size_handle = Context->logger_size_handle ;      \
185             (*logger_handle) = GB_MALLOC (GB_LOGGER_LEN+1, char,            \
186                 logger_size_handle) ;                                       \
187             if ((*logger_handle) != NULL)                                   \
188             {                                                               \
189                 snprintf ((*logger_handle), GB_LOGGER_LEN,                  \
190                     "GraphBLAS error: %s\nfunction: %s\n" format,           \
191                     GB_status_code (info), Context->where, __VA_ARGS__) ;   \
192             }                                                               \
193         }                                                                   \
194     }                                                                       \
195     return (info) ;                                                         \
196 }
197 
198 #endif
199 
200