| 1 | #ifndef _CIVL_COLLATE_
|
|---|
| 2 | #define _CIVL_COLLATE_
|
|---|
| 3 |
|
|---|
| 4 | #include<collate.cvh>
|
|---|
| 5 | #include<civlc.cvh>
|
|---|
| 6 | #include<seq.cvh>
|
|---|
| 7 |
|
|---|
| 8 | // Status of a collate state for a process:
|
|---|
| 9 | #define NONARRIVED 0
|
|---|
| 10 | #define ARRIVED 1
|
|---|
| 11 | #define DEPARTED 2
|
|---|
| 12 | // Status of a collate attribute checker:
|
|---|
| 13 | #define UNCHECKED 3
|
|---|
| 14 | #define CHECKED 4
|
|---|
| 15 | // The $gcollate_state whose state field is significant:
|
|---|
| 16 | #define _STATE_USED(gstate, place) ((gstate)->status[(place)] >= NONARRIVED && (gstate)->status[(place)] <= DEPARTED)
|
|---|
| 17 | #define _ARRIVED(gstate, place) ((gstate)->status[(place)] == ARRIVED || (gstate)->status[(place)] == DEPARTED)
|
|---|
| 18 | #define _DEPARTED(gstate, place) ((gstate)->status[(place)] == DEPARTED)
|
|---|
| 19 | // The $gcollate_state whose attribute field is significant:
|
|---|
| 20 | #define _ATTR_USED(gstate, place) ((gstate)->status[(place)] >= UNCHECKED && (gstate)->status[(place)] <= CHECKED)
|
|---|
| 21 | #define _CHECKED(gstate, place) ((gstate)->status[(place)] == CHECKED)
|
|---|
| 22 |
|
|---|
| 23 | /******************* Definition of datatypes: *******************/
|
|---|
| 24 | struct _gcollator {
|
|---|
| 25 | // The number of participants of a _gcollator object:
|
|---|
| 26 | int nprocs;
|
|---|
| 27 | // $proc array:
|
|---|
| 28 | $proc procs[];
|
|---|
| 29 | // The length of the queue of collation states:
|
|---|
| 30 | int queue_length;
|
|---|
| 31 | // The queue of collation states. Note that elements in this queue
|
|---|
| 32 | // are references to collate state objects:
|
|---|
| 33 | $gcollate_state queue[];
|
|---|
| 34 | _Bool entered[];
|
|---|
| 35 | };
|
|---|
| 36 |
|
|---|
| 37 | struct _collator {
|
|---|
| 38 | // The place of the process in a _gcollator who holds this handle:
|
|---|
| 39 | int place;
|
|---|
| 40 | // A handle to the _gcollator object:
|
|---|
| 41 | $gcollator gcollator;
|
|---|
| 42 | };
|
|---|
| 43 |
|
|---|
| 44 | // TODO: don't use one queue for collate states and collective attributes
|
|---|
| 45 | struct _gcollate_state {
|
|---|
| 46 | // An array of markers for whether a process has already arrived
|
|---|
| 47 | // this entry or departed:
|
|---|
| 48 | int status[];
|
|---|
| 49 | // collate $state:
|
|---|
| 50 | $state state;
|
|---|
| 51 | // A customized attribute, it can be used to check some attribute
|
|---|
| 52 | // that must be equivalent collectively at some point:
|
|---|
| 53 | $bundle attribute;
|
|---|
| 54 | // Note: for one $gcollate_state object, state and attribute cannot
|
|---|
| 55 | // be in use at the same time.
|
|---|
| 56 | };
|
|---|
| 57 |
|
|---|
| 58 | struct _collate_state {
|
|---|
| 59 | // The place of the process in a _gcollator who holds this handle:
|
|---|
| 60 | int place;
|
|---|
| 61 | // A reference to a _gcollate_state:
|
|---|
| 62 | $gcollate_state gstate;
|
|---|
| 63 | };
|
|---|
| 64 |
|
|---|
| 65 | /******************* Function definitions ***************************/
|
|---|
| 66 | /* Creates an global collator object. The object is allocated in the
|
|---|
| 67 | * given scope. It returns a handle $gcollator to the object.
|
|---|
| 68 | *
|
|---|
| 69 | * scope : The scope where the object is allocated.
|
|---|
| 70 | * nprocs: The number of processes included in the global collator.
|
|---|
| 71 | */
|
|---|
| 72 | $atomic_f $gcollator $gcollator_create($scope scope, int nprocs) {
|
|---|
| 73 | $gcollator gcollator = ($gcollator)$malloc(scope, sizeof(struct _gcollator));
|
|---|
| 74 |
|
|---|
| 75 | gcollator->nprocs = nprocs;
|
|---|
| 76 | gcollator->procs = ($proc[nprocs])$lambda(int i) $proc_null;
|
|---|
| 77 | gcollator->queue_length = 0;
|
|---|
| 78 | $seq_init(&(gcollator->queue), 0, NULL);
|
|---|
| 79 | gcollator->entered=(_Bool[nprocs])$lambda(int i) $false;
|
|---|
| 80 | return gcollator;
|
|---|
| 81 | }
|
|---|
| 82 |
|
|---|
| 83 | /* Free a $gcollator object. All gcollate states referred by this
|
|---|
| 84 | * object will be freed as well.
|
|---|
| 85 | *
|
|---|
| 86 | * gc : a handle to the $gcollator object
|
|---|
| 87 | */
|
|---|
| 88 | $atomic_f void $gcollator_destroy($gcollator gc) {
|
|---|
| 89 | $assert(gc->queue_length == 0, "There is at least an entry in the gcollator "
|
|---|
| 90 | "(referred by %p) hasn't been departed by all processes.", gc);
|
|---|
| 91 | int garbage_size = $seq_length(&gc->queue);
|
|---|
| 92 |
|
|---|
| 93 | for (int i = 0; i < garbage_size; i++)
|
|---|
| 94 | $free(gc->queue[i]);
|
|---|
| 95 | $free(gc);
|
|---|
| 96 | }
|
|---|
| 97 |
|
|---|
| 98 | /* Creates a local collate object. The object is allocated in the
|
|---|
| 99 | * given scope. A local collate represents a local handle to part of
|
|---|
| 100 | * the global collate object.
|
|---|
| 101 | *
|
|---|
| 102 | * gcollator: The handle to the global collate object.
|
|---|
| 103 | * scope: The scope where the local collate object will be allocated.
|
|---|
| 104 | * place: The place of the participant process in the global collate
|
|---|
| 105 | * object. Each participant process has an unique place. Place shall
|
|---|
| 106 | * be greater than or equal to 0 and less than the number of all
|
|---|
| 107 | * participants.
|
|---|
| 108 | */
|
|---|
| 109 | $atomic_f $collator $collator_create($gcollator gcollator, $scope scope, int place) {
|
|---|
| 110 | $collator collator = ($collator)$malloc(scope, sizeof(struct _collator));
|
|---|
| 111 |
|
|---|
| 112 | collator->place = place;
|
|---|
| 113 | collator->gcollator = gcollator;
|
|---|
| 114 | collator->gcollator->procs[place] = $self;
|
|---|
| 115 | return collator;
|
|---|
| 116 | }
|
|---|
| 117 |
|
|---|
| 118 | $atomic_f void $collator_enters($collator collator){
|
|---|
| 119 | collator->gcollator->entered[collator->place] = $true;
|
|---|
| 120 | }
|
|---|
| 121 |
|
|---|
| 122 | $atomic_f _Bool $collator_has_entered($collator collator, $range range){
|
|---|
| 123 | $domain(1) dom=($domain(1)){range};
|
|---|
| 124 | _Bool *entered=collator->gcollator->entered;
|
|---|
| 125 |
|
|---|
| 126 | $for(int i: dom){
|
|---|
| 127 | if(!entered[i])
|
|---|
| 128 | return $false;
|
|---|
| 129 | }
|
|---|
| 130 | return $true;
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| 133 | $atomic_f $collate_state $collate_arrives($collator c, $scope scope) {
|
|---|
| 134 | int place = c->place;
|
|---|
| 135 | int queue_size, nprocs;
|
|---|
| 136 | _Bool first = $true;
|
|---|
| 137 | $gcollate_state * queue;
|
|---|
| 138 | $gcollate_state gcollate_state;
|
|---|
| 139 | $collate_state result = {0, NULL};
|
|---|
| 140 |
|
|---|
| 141 | nprocs = c->gcollator->nprocs;
|
|---|
| 142 | queue = c->gcollator->queue;
|
|---|
| 143 | queue_size = c->gcollator->queue_length;
|
|---|
| 144 |
|
|---|
| 145 | // Looking for the first unarrived collate state. If no such a collate
|
|---|
| 146 | // state, create and insert one:
|
|---|
| 147 | for (int i = 0; i < queue_size; i++)
|
|---|
| 148 | if (_STATE_USED(queue[i], place) && !_ARRIVED(queue[i], place)) {
|
|---|
| 149 | gcollate_state = queue[i];
|
|---|
| 150 | first = $false;
|
|---|
| 151 | break;
|
|---|
| 152 | }
|
|---|
| 153 | if (first) {
|
|---|
| 154 | _Bool initValue = $false;
|
|---|
| 155 | $scope root = $scopeof(*c->gcollator);
|
|---|
| 156 |
|
|---|
| 157 | gcollate_state = ($gcollate_state)$malloc(root, sizeof(struct _gcollate_state));
|
|---|
| 158 | gcollate_state->status = (int[nprocs])$lambda(int i) NONARRIVED;
|
|---|
| 159 | gcollate_state->state = $state_null;
|
|---|
| 160 | // It must use insertion instead of append given the existance of
|
|---|
| 161 | // garbage section:
|
|---|
| 162 | $seq_insert(&c->gcollator->queue, c->gcollator->queue_length, &gcollate_state, 1);
|
|---|
| 163 | c->gcollator->queue_length++;
|
|---|
| 164 | }
|
|---|
| 165 | result.place = place;
|
|---|
| 166 | result.gstate = gcollate_state;
|
|---|
| 167 | $collate_snapshot(result, nprocs, scope);
|
|---|
| 168 | gcollate_state->status[place] = ARRIVED;
|
|---|
| 169 | return result;
|
|---|
| 170 | }
|
|---|
| 171 |
|
|---|
| 172 | $atomic_f void $collate_departs($collator c, $collate_state cs) {
|
|---|
| 173 | int nprocs, queue_size, place, index;
|
|---|
| 174 | $gcollate_state *queue;
|
|---|
| 175 | $gcollate_state gcollate_state = cs.gstate;
|
|---|
| 176 |
|
|---|
| 177 | place = c->place;
|
|---|
| 178 | nprocs = c->gcollator->nprocs;
|
|---|
| 179 | queue_size = c->gcollator->queue_length;
|
|---|
| 180 | queue = c->gcollator->queue;
|
|---|
| 181 | $assert(_STATE_USED(gcollate_state, place)
|
|---|
| 182 | && _ARRIVED(gcollate_state, place),
|
|---|
| 183 | "Only an arrived collate state can be freed");
|
|---|
| 184 | // Mark the refered gcollate state as departed:
|
|---|
| 185 | gcollate_state->status[place] = DEPARTED;
|
|---|
| 186 |
|
|---|
| 187 | /* For a gcollate state which has been departed by all processes, it
|
|---|
| 188 | * will be move to the tail of queue. Then there forms a garbage
|
|---|
| 189 | * section at the tail of the queue whose length will increase. The
|
|---|
| 190 | * reason of keeping these departed gcollate states is they still
|
|---|
| 191 | * can be read by processes after they are marked as departed. The garbage
|
|---|
| 192 | * section will be freed when the gcollator being destroyed.
|
|---|
| 193 | *
|
|---|
| 194 | * The queue length will not reveal the existance of the garbage
|
|---|
| 195 | * section, i.e. queue length will decrease by 1 after a gcollate
|
|---|
| 196 | * state being moved to the garbage section.
|
|---|
| 197 | */
|
|---|
| 198 |
|
|---|
| 199 | /* Scan the queue in the gcollator to remove the entry that just has
|
|---|
| 200 | * been marked as DEPARTED by all processes (all-departed). At this
|
|---|
| 201 | * point, there is at most one all-departed entry in the queue. */
|
|---|
| 202 | index = -1;
|
|---|
| 203 | for (int i = 0; i < queue_size; i++) {
|
|---|
| 204 | _Bool isGarbage = $true;
|
|---|
| 205 |
|
|---|
| 206 | for (int j = 0; j < nprocs; j++)
|
|---|
| 207 | if (!_DEPARTED(queue[i], j)) {
|
|---|
| 208 | isGarbage = $false;
|
|---|
| 209 | break;
|
|---|
| 210 | }
|
|---|
| 211 | if (isGarbage) {
|
|---|
| 212 | index = i;
|
|---|
| 213 | break;
|
|---|
| 214 | }
|
|---|
| 215 | }
|
|---|
| 216 | if (index >= 0) {
|
|---|
| 217 | $seq_remove(&c->gcollator->queue, index, NULL, 1);
|
|---|
| 218 | $seq_append(&c->gcollator->queue, &gcollate_state, 1);
|
|---|
| 219 | c->gcollator->queue_length--;
|
|---|
| 220 | }
|
|---|
| 221 | // set the gcollate state reference to NULL so that the process
|
|---|
| 222 | // cannot access it after the call of departs():
|
|---|
| 223 | cs.gstate = NULL;
|
|---|
| 224 | }
|
|---|
| 225 |
|
|---|
| 226 | $atomic_f $bundle $collate_check($collator c, $bundle bundle) {
|
|---|
| 227 | int nprocs, queue_size, place, index;
|
|---|
| 228 | _Bool first = $true;
|
|---|
| 229 | $gcollate_state attr;
|
|---|
| 230 | $bundle result;
|
|---|
| 231 |
|
|---|
| 232 | nprocs = c->gcollator->nprocs;
|
|---|
| 233 | place = c->place;
|
|---|
| 234 | queue_size = c->gcollator->queue_length;
|
|---|
| 235 | for (int i = 0; i < queue_size; i++) {
|
|---|
| 236 | if (_ATTR_USED(c->gcollator->queue[i], place)
|
|---|
| 237 | && !_CHECKED(c->gcollator->queue[i], place)) {
|
|---|
| 238 | first = $false;
|
|---|
| 239 | attr = c->gcollator->queue[i];
|
|---|
| 240 | index = i;
|
|---|
| 241 | break;
|
|---|
| 242 | }
|
|---|
| 243 | }
|
|---|
| 244 |
|
|---|
| 245 | if (first) {
|
|---|
| 246 | $scope gcollate_scope = $scopeof(*c->gcollator);
|
|---|
| 247 |
|
|---|
| 248 | attr = ($gcollate_state)$malloc(gcollate_scope, sizeof(struct _gcollate_state));
|
|---|
| 249 | attr->status = ((int[nprocs])$lambda(int i)UNCHECKED);
|
|---|
| 250 | attr->attribute = bundle;
|
|---|
| 251 | // It must use insertion instead of append given the existance of
|
|---|
| 252 | // garbage section:
|
|---|
| 253 | $seq_insert(&c->gcollator->queue, c->gcollator->queue_length, &attr, 1);
|
|---|
| 254 | index = c->gcollator->queue_length++;
|
|---|
| 255 | }
|
|---|
| 256 | attr->status[place] = CHECKED;
|
|---|
| 257 | result = attr->attribute;
|
|---|
| 258 |
|
|---|
| 259 | // if complete, dequeue:
|
|---|
| 260 | _Bool complete = $true;
|
|---|
| 261 |
|
|---|
| 262 | for (int i = 0; i < nprocs; i++)
|
|---|
| 263 | if (!_CHECKED(attr, i)) {
|
|---|
| 264 | complete = $false;
|
|---|
| 265 | break;
|
|---|
| 266 | }
|
|---|
| 267 | if (complete) {
|
|---|
| 268 | $free(c->gcollator->queue[index]);
|
|---|
| 269 | $seq_remove(&c->gcollator->queue, index, NULL, 1);
|
|---|
| 270 | c->gcollator->queue_length--;
|
|---|
| 271 | }
|
|---|
| 272 | return result;
|
|---|
| 273 | }
|
|---|
| 274 |
|
|---|
| 275 | $atomic_f $state_f $state * $collate_get_state($collate_state state){
|
|---|
| 276 | return &state.gstate->state;
|
|---|
| 277 | }
|
|---|
| 278 |
|
|---|
| 279 | #endif
|
|---|