| 1 | //
|
|---|
| 2 | // messshm.cc
|
|---|
| 3 | //
|
|---|
| 4 | // Copyright (C) 1996 Limit Point Systems, Inc.
|
|---|
| 5 | //
|
|---|
| 6 | // Author: Curtis Janssen <cljanss@limitpt.com>
|
|---|
| 7 | // Maintainer: LPS
|
|---|
| 8 | //
|
|---|
| 9 | // This file is part of the SC Toolkit.
|
|---|
| 10 | //
|
|---|
| 11 | // The SC Toolkit is free software; you can redistribute it and/or modify
|
|---|
| 12 | // it under the terms of the GNU Library General Public License as published by
|
|---|
| 13 | // the Free Software Foundation; either version 2, or (at your option)
|
|---|
| 14 | // any later version.
|
|---|
| 15 | //
|
|---|
| 16 | // The SC Toolkit is distributed in the hope that it will be useful,
|
|---|
| 17 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|---|
| 18 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|---|
| 19 | // GNU Library General Public License for more details.
|
|---|
| 20 | //
|
|---|
| 21 | // You should have received a copy of the GNU Library General Public License
|
|---|
| 22 | // along with the SC Toolkit; see the file COPYING.LIB. If not, write to
|
|---|
| 23 | // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
|
|---|
| 24 | //
|
|---|
| 25 | // The U.S. Government is granted a limited license as per AL 91-7.
|
|---|
| 26 | //
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 | #include <unistd.h>
|
|---|
| 30 | #include <sys/types.h>
|
|---|
| 31 | #include <sys/ipc.h>
|
|---|
| 32 | #include <sys/sem.h>
|
|---|
| 33 | #include <sys/shm.h>
|
|---|
| 34 |
|
|---|
| 35 |
|
|---|
| 36 | #include <util/misc/bug.h>
|
|---|
| 37 | #include <util/misc/formio.h>
|
|---|
| 38 | #include <util/group/messshm.h>
|
|---|
| 39 |
|
|---|
| 40 | using namespace std;
|
|---|
| 41 | using namespace sc;
|
|---|
| 42 |
|
|---|
| 43 | //#define DEBUG
|
|---|
| 44 |
|
|---|
| 45 | #ifndef SEM_A
|
|---|
| 46 | # define SEM_A 0200
|
|---|
| 47 | #endif
|
|---|
| 48 |
|
|---|
| 49 | #ifndef SEM_R
|
|---|
| 50 | # define SEM_R 0400
|
|---|
| 51 | #endif
|
|---|
| 52 |
|
|---|
| 53 | /* NALIGN is the byte boundary that we align data on. */
|
|---|
| 54 | #define NALIGN 8
|
|---|
| 55 | #define ROUNDUPTOALIGN(n) (((n) + (NALIGN-1)) & ~(NALIGN-1))
|
|---|
| 56 |
|
|---|
| 57 | static ClassDesc ShmMessageGrp_cd(
|
|---|
| 58 | typeid(ShmMessageGrp),"ShmMessageGrp",1,"public intMessageGrp",
|
|---|
| 59 | 0, create<ShmMessageGrp>, 0);
|
|---|
| 60 |
|
|---|
| 61 | ShmMessageGrp::ShmMessageGrp()
|
|---|
| 62 | {
|
|---|
| 63 | initialize();
|
|---|
| 64 | }
|
|---|
| 65 |
|
|---|
| 66 | ShmMessageGrp::ShmMessageGrp(int nprocs)
|
|---|
| 67 | {
|
|---|
| 68 | initialize(nprocs);
|
|---|
| 69 | }
|
|---|
| 70 |
|
|---|
| 71 | ShmMessageGrp::ShmMessageGrp(const Ref<KeyVal>& keyval):
|
|---|
| 72 | intMessageGrp(keyval)
|
|---|
| 73 | {
|
|---|
| 74 | int nprocs = keyval->intvalue("n");
|
|---|
| 75 | if (keyval->error() != KeyVal::OK) initialize();
|
|---|
| 76 | else initialize(nprocs);
|
|---|
| 77 | }
|
|---|
| 78 |
|
|---|
| 79 | void ShmMessageGrp::sync()
|
|---|
| 80 | {
|
|---|
| 81 | int i;
|
|---|
| 82 | for (i=0; i<n(); i++) {
|
|---|
| 83 | if (me() == i) continue;
|
|---|
| 84 | wait_for_write(i);
|
|---|
| 85 | commbuf[i]->n_sync++;
|
|---|
| 86 | if (commbuf[i]->n_sync >= n()-1) {
|
|---|
| 87 | while(commbuf[i]->n_wait_for_change) {
|
|---|
| 88 | put_change(i);
|
|---|
| 89 | commbuf[i]->n_wait_for_change--;
|
|---|
| 90 | }
|
|---|
| 91 | }
|
|---|
| 92 | release_write(i);
|
|---|
| 93 | }
|
|---|
| 94 | wait_for_write(me());
|
|---|
| 95 | while (commbuf[me()]->n_sync < n()-1) {
|
|---|
| 96 | commbuf[me()]->n_wait_for_change++;
|
|---|
| 97 | release_write(me());
|
|---|
| 98 | get_change(me());
|
|---|
| 99 | wait_for_write(me());
|
|---|
| 100 | }
|
|---|
| 101 | commbuf[me()]->n_sync -= n()-1;
|
|---|
| 102 | while(commbuf[me()]->n_wait_for_change) {
|
|---|
| 103 | put_change(me());
|
|---|
| 104 | commbuf[me()]->n_wait_for_change--;
|
|---|
| 105 | }
|
|---|
| 106 | release_write(me());
|
|---|
| 107 | }
|
|---|
| 108 |
|
|---|
| 109 | ShmMessageGrp::~ShmMessageGrp()
|
|---|
| 110 | {
|
|---|
| 111 | // sync the nodes
|
|---|
| 112 | sync();
|
|---|
| 113 |
|
|---|
| 114 | // make sure node zero is las to touch the shared memory
|
|---|
| 115 | if (me() == 0) {
|
|---|
| 116 | wait_for_write(0);
|
|---|
| 117 | while (commbuf[0]->n_sync < n()-1) {
|
|---|
| 118 | commbuf[0]->n_wait_for_change++;
|
|---|
| 119 | release_write(0);
|
|---|
| 120 | get_change(0);
|
|---|
| 121 | wait_for_write(0);
|
|---|
| 122 | }
|
|---|
| 123 | release_write(0);
|
|---|
| 124 | shmdt((SHMTYPE)sharedmem);
|
|---|
| 125 | // release the memory
|
|---|
| 126 | shmctl(shmid,IPC_RMID,0);
|
|---|
| 127 |
|
|---|
| 128 | for (int i=0; i<n(); i++) {
|
|---|
| 129 | #ifdef SEMCTL_REQUIRES_SEMUN
|
|---|
| 130 | semun junk;
|
|---|
| 131 | junk.val = 0;
|
|---|
| 132 | #else
|
|---|
| 133 | int junk = 0;
|
|---|
| 134 | #endif
|
|---|
| 135 | semctl(semid,i,IPC_RMID,junk);
|
|---|
| 136 | semctl(change_semid,i,IPC_RMID,junk);
|
|---|
| 137 | }
|
|---|
| 138 | }
|
|---|
| 139 | else {
|
|---|
| 140 | wait_for_write(0);
|
|---|
| 141 | commbuf[0]->n_sync++;
|
|---|
| 142 | while(commbuf[0]->n_wait_for_change) {
|
|---|
| 143 | put_change(0);
|
|---|
| 144 | commbuf[0]->n_wait_for_change--;
|
|---|
| 145 | }
|
|---|
| 146 | shmdt((SHMTYPE)sharedmem);
|
|---|
| 147 | release_write(0);
|
|---|
| 148 | }
|
|---|
| 149 | }
|
|---|
| 150 |
|
|---|
| 151 | void ShmMessageGrp::initialize()
|
|---|
| 152 | {
|
|---|
| 153 | int nprocs = atoi(getenv("NUMPROC"));
|
|---|
| 154 | if (!nprocs) nprocs = 1;
|
|---|
| 155 | initialize(nprocs);
|
|---|
| 156 | }
|
|---|
| 157 |
|
|---|
| 158 | void ShmMessageGrp::initialize(int nprocs)
|
|---|
| 159 | {
|
|---|
| 160 | int i;
|
|---|
| 161 | void* nextbuf;
|
|---|
| 162 |
|
|---|
| 163 | semdec.sem_num = 0;
|
|---|
| 164 | semdec.sem_op = -1;
|
|---|
| 165 | semdec.sem_flg = 0;
|
|---|
| 166 | seminc.sem_num = 0;
|
|---|
| 167 | seminc.sem_op = 1;
|
|---|
| 168 | seminc.sem_flg = 0;
|
|---|
| 169 |
|
|---|
| 170 | // Each node gets a buffer for incoming data.
|
|---|
| 171 | shmid = shmget(IPC_PRIVATE,
|
|---|
| 172 | nprocs * sizeof(commbuf_t),
|
|---|
| 173 | IPC_CREAT | SHM_R | SHM_W);
|
|---|
| 174 |
|
|---|
| 175 | // Attach the shared segment.
|
|---|
| 176 | nextbuf = sharedmem = shmat(shmid,0,0);
|
|---|
| 177 |
|
|---|
| 178 | #ifdef SEMCTL_REQUIRES_SEMUN
|
|---|
| 179 | semun semzero;
|
|---|
| 180 | semzero.val = 0;
|
|---|
| 181 | semun semone;
|
|---|
| 182 | semone.val = 1;
|
|---|
| 183 | #else
|
|---|
| 184 | int semzero = 0;
|
|---|
| 185 | int semone = 1;
|
|---|
| 186 | #endif
|
|---|
| 187 |
|
|---|
| 188 | // Each shared memory segment gets a semaphore to synchronize access.
|
|---|
| 189 | semid = semget(IPC_PRIVATE,nprocs,IPC_CREAT | SEM_R | SEM_A );
|
|---|
| 190 | if (semid == -1) {
|
|---|
| 191 | perror("semget");
|
|---|
| 192 | exit(-1);
|
|---|
| 193 | }
|
|---|
| 194 |
|
|---|
| 195 | change_semid = semget(IPC_PRIVATE,nprocs,IPC_CREAT | SEM_R | SEM_A );
|
|---|
| 196 | if (change_semid == -1) {
|
|---|
| 197 | perror("semget");
|
|---|
| 198 | exit(-1);
|
|---|
| 199 | }
|
|---|
| 200 |
|
|---|
| 201 | for (i=0; i<nprocs; i++) {
|
|---|
| 202 |
|
|---|
| 203 | // Mark all of the segments as available for writing.
|
|---|
| 204 | if (semctl(semid,i,SETVAL,semone) == -1) {
|
|---|
| 205 | perror("semctl");
|
|---|
| 206 | exit(-1);
|
|---|
| 207 | }
|
|---|
| 208 |
|
|---|
| 209 | if (semctl(change_semid,i,SETVAL,semzero) == -1) {
|
|---|
| 210 | perror("semctl");
|
|---|
| 211 | exit(-1);
|
|---|
| 212 | }
|
|---|
| 213 |
|
|---|
| 214 | // Alloc shm for each node's commbuf.
|
|---|
| 215 | commbuf[i] = (commbuf_t*) nextbuf;
|
|---|
| 216 | // Mark the commbuf as having no messages.
|
|---|
| 217 | commbuf[i]->nmsg = 0;
|
|---|
| 218 | // and no outstanding waits
|
|---|
| 219 | commbuf[i]->n_wait_for_change = 0;
|
|---|
| 220 | commbuf[i]->n_sync = 0;
|
|---|
| 221 | nextbuf = (void*)(((char*)nextbuf) + sizeof(commbuf_t));
|
|---|
| 222 | }
|
|---|
| 223 |
|
|---|
| 224 | // Create the remaining nodes.
|
|---|
| 225 | int mynodeid = 0;
|
|---|
| 226 | for (i=1; i<nprocs; i++) {
|
|---|
| 227 | int pid = fork();
|
|---|
| 228 | if (!pid) {
|
|---|
| 229 | mynodeid = i;
|
|---|
| 230 | break;
|
|---|
| 231 | }
|
|---|
| 232 | }
|
|---|
| 233 |
|
|---|
| 234 | // Initialize the base class.
|
|---|
| 235 | intMessageGrp::initialize(mynodeid, nprocs, 30);
|
|---|
| 236 | }
|
|---|
| 237 |
|
|---|
| 238 | Ref<MessageGrp>
|
|---|
| 239 | ShmMessageGrp::clone(void)
|
|---|
| 240 | {
|
|---|
| 241 | Ref<MessageGrp> smgrp = new ShmMessageGrp(n());
|
|---|
| 242 | return smgrp;
|
|---|
| 243 | }
|
|---|
| 244 |
|
|---|
| 245 | int ShmMessageGrp::basic_probe(int msgtype)
|
|---|
| 246 | {
|
|---|
| 247 | int i;
|
|---|
| 248 | msgbuf_t *message;
|
|---|
| 249 |
|
|---|
| 250 | wait_for_write(me());
|
|---|
| 251 |
|
|---|
| 252 | message = (msgbuf_t*)commbuf[me()]->buf;
|
|---|
| 253 | for (i=0; i<commbuf[me()]->nmsg; i++) {
|
|---|
| 254 | if ((msgtype == -1) || (msgtype == message->type)) {
|
|---|
| 255 | release_write(me());
|
|---|
| 256 | return 1;
|
|---|
| 257 | }
|
|---|
| 258 | message = NEXT_MESSAGE(message);
|
|---|
| 259 | }
|
|---|
| 260 |
|
|---|
| 261 | release_write(me());
|
|---|
| 262 |
|
|---|
| 263 | return 0;
|
|---|
| 264 | }
|
|---|
| 265 |
|
|---|
| 266 | void ShmMessageGrp::basic_recv(int type, void* buf, int bytes)
|
|---|
| 267 | {
|
|---|
| 268 | int size;
|
|---|
| 269 | int i;
|
|---|
| 270 | msgbuf_t *message,*lastmessage;
|
|---|
| 271 |
|
|---|
| 272 | #ifdef DEBUG
|
|---|
| 273 | ExEnv::outn() << "ShmGrp: basic_recv: "
|
|---|
| 274 | << "type = " << type << ' '
|
|---|
| 275 | << "buf = " << buf << ' '
|
|---|
| 276 | << "bytes = " << bytes << ' '
|
|---|
| 277 | << "me = " << me() << endl;
|
|---|
| 278 | print_buffer(me(),me());
|
|---|
| 279 | #endif
|
|---|
| 280 |
|
|---|
| 281 | look_for_message:
|
|---|
| 282 |
|
|---|
| 283 | wait_for_write(me());
|
|---|
| 284 |
|
|---|
| 285 | message = (msgbuf_t*)commbuf[me()]->buf;
|
|---|
| 286 | for (i=0; i<commbuf[me()]->nmsg; i++) {
|
|---|
| 287 | if (message->type == type) break;
|
|---|
| 288 | message = NEXT_MESSAGE(message);
|
|---|
| 289 | }
|
|---|
| 290 | if (i==commbuf[me()]->nmsg) {
|
|---|
| 291 | commbuf[me()]->n_wait_for_change++;
|
|---|
| 292 | release_write(me());
|
|---|
| 293 | get_change(me());
|
|---|
| 294 | goto look_for_message;
|
|---|
| 295 | }
|
|---|
| 296 |
|
|---|
| 297 | if (bytes < message->size) {
|
|---|
| 298 | ExEnv::errn() << scprintf("messshm.cc: recv buffer too small\n");
|
|---|
| 299 | abort();
|
|---|
| 300 | }
|
|---|
| 301 | if (bytes < message->size) size = bytes;
|
|---|
| 302 | else size = message->size;
|
|---|
| 303 |
|
|---|
| 304 | // Copy the data.
|
|---|
| 305 | memcpy(buf,((char*)message) + sizeof(msgbuf_t),size);
|
|---|
| 306 |
|
|---|
| 307 | // Find the lastmessage.
|
|---|
| 308 | lastmessage = (msgbuf_t*)commbuf[me()]->buf;
|
|---|
| 309 | for (i=0; i<commbuf[me()]->nmsg; i++) {
|
|---|
| 310 | lastmessage = NEXT_MESSAGE(lastmessage);
|
|---|
| 311 | }
|
|---|
| 312 |
|
|---|
| 313 | // Repack the message buffer.
|
|---|
| 314 | memmove(message,NEXT_MESSAGE(message),
|
|---|
| 315 | (size_t)((char*)lastmessage)-(size_t)((char*)NEXT_MESSAGE(message)));
|
|---|
| 316 |
|
|---|
| 317 | commbuf[me()]->nmsg--;
|
|---|
| 318 |
|
|---|
| 319 | while(commbuf[me()]->n_wait_for_change) {
|
|---|
| 320 | put_change(me());
|
|---|
| 321 | commbuf[me()]->n_wait_for_change--;
|
|---|
| 322 | }
|
|---|
| 323 |
|
|---|
| 324 | release_write(me());
|
|---|
| 325 | }
|
|---|
| 326 |
|
|---|
| 327 | void ShmMessageGrp::basic_send(int dest, int type, const void* buf, int bytes)
|
|---|
| 328 | {
|
|---|
| 329 | int i;
|
|---|
| 330 | msgbuf_t *availmsg;
|
|---|
| 331 |
|
|---|
| 332 | #ifdef DEBUG
|
|---|
| 333 | ExEnv::outn() << "ShmGrp: basic_send: "
|
|---|
| 334 | << "dest = " << dest << ' '
|
|---|
| 335 | << "type = " << type << ' '
|
|---|
| 336 | << "buf = " << buf << ' '
|
|---|
| 337 | << "bytes = " << bytes << ' '
|
|---|
| 338 | << "me = " << me() << endl;
|
|---|
| 339 | #endif
|
|---|
| 340 |
|
|---|
| 341 | if (dest>=n()) {
|
|---|
| 342 | //debug_start("bad destination");
|
|---|
| 343 | ExEnv::errn() << scprintf("ShmMessageGrp::basic_send: bad destination\n");
|
|---|
| 344 | abort();
|
|---|
| 345 | }
|
|---|
| 346 |
|
|---|
| 347 | try_send_again:
|
|---|
| 348 |
|
|---|
| 349 | // Obtain write access to the dest's incoming buffer.
|
|---|
| 350 | wait_for_write(dest);
|
|---|
| 351 |
|
|---|
| 352 | availmsg = (msgbuf_t*)commbuf[dest]->buf;
|
|---|
| 353 | for (i=0; i<commbuf[dest]->nmsg; i++) {
|
|---|
| 354 | availmsg = NEXT_MESSAGE(availmsg);
|
|---|
| 355 | }
|
|---|
| 356 | if ( (((char*)availmsg) + ROUNDUPTOALIGN(sizeof(msgbuf_t) + bytes))
|
|---|
| 357 | > (((char*)commbuf[dest]) + sizeof(commbuf_t))) {
|
|---|
| 358 | if (me() == dest) {
|
|---|
| 359 | // sending a message to myself and the buffer is full
|
|---|
| 360 | // --cannot recover
|
|---|
| 361 | ExEnv::errn() << scprintf("commbuf size exceeded on %d\n",me());
|
|---|
| 362 | ExEnv::errn() << scprintf(" availmsg = 0x%x\n",availmsg);
|
|---|
| 363 | ExEnv::errn() << scprintf(" commbuf[%d] + sizeof(commbuf_t) = 0x%x\n",
|
|---|
| 364 | dest,((char*)commbuf[dest]) + sizeof(commbuf_t));
|
|---|
| 365 | ExEnv::errn() << scprintf(" size = %d\n",bytes);
|
|---|
| 366 | abort();
|
|---|
| 367 | }
|
|---|
| 368 | else {
|
|---|
| 369 | // try to recover from a full buffer by waiting for the dest
|
|---|
| 370 | // to read some data.
|
|---|
| 371 | commbuf[dest]->n_wait_for_change++;
|
|---|
| 372 | release_write(dest);
|
|---|
| 373 | get_change(dest);
|
|---|
| 374 | goto try_send_again;
|
|---|
| 375 | }
|
|---|
| 376 | }
|
|---|
| 377 | availmsg->from = me();
|
|---|
| 378 | availmsg->type = type;
|
|---|
| 379 | availmsg->size = bytes;
|
|---|
| 380 | memcpy(((char*)availmsg) + sizeof(msgbuf_t),buf,bytes);
|
|---|
| 381 | commbuf[dest]->nmsg++;
|
|---|
| 382 |
|
|---|
| 383 | // let the dest know that there is more data in the buffer
|
|---|
| 384 | while(commbuf[dest]->n_wait_for_change) {
|
|---|
| 385 | put_change(dest);
|
|---|
| 386 | commbuf[dest]->n_wait_for_change--;
|
|---|
| 387 | }
|
|---|
| 388 |
|
|---|
| 389 | // Release write access to the dest's buffer.
|
|---|
| 390 | release_write(dest);
|
|---|
| 391 | }
|
|---|
| 392 |
|
|---|
| 393 | msgbuf_t * ShmMessageGrp::NEXT_MESSAGE(msgbuf_t *m)
|
|---|
| 394 | {
|
|---|
| 395 | msgbuf_t *r;
|
|---|
| 396 | if (m->size < 0) {
|
|---|
| 397 | ExEnv::errn() << scprintf("NEXT_MESSAGE: m->size = %d (real %d)\n",
|
|---|
| 398 | m->size,sizeof(msgbuf_t) + m->size + m->size%8);
|
|---|
| 399 | //debug_start("m->size < 0");
|
|---|
| 400 | ExEnv::errn() << scprintf("messshm.cc: m->size < 0\n");
|
|---|
| 401 | abort();
|
|---|
| 402 | }
|
|---|
| 403 | r = ((msgbuf_t*)(((char*)m) + ROUNDUPTOALIGN(sizeof(msgbuf_t) + m->size)));
|
|---|
| 404 | return r;
|
|---|
| 405 | }
|
|---|
| 406 |
|
|---|
| 407 | void ShmMessageGrp::get_change(int node)
|
|---|
| 408 | {
|
|---|
| 409 | semdec.sem_num = node;
|
|---|
| 410 | semop(change_semid,&semdec,1);
|
|---|
| 411 | semdec.sem_num = 0;
|
|---|
| 412 | }
|
|---|
| 413 |
|
|---|
| 414 | void ShmMessageGrp::put_change(int node)
|
|---|
| 415 | {
|
|---|
| 416 | seminc.sem_num = node;
|
|---|
| 417 | semop(change_semid,&seminc,1);
|
|---|
| 418 | seminc.sem_num = 0;
|
|---|
| 419 |
|
|---|
| 420 | }
|
|---|
| 421 |
|
|---|
| 422 | // Obtain a lock for writing to the node's buffer.
|
|---|
| 423 | void ShmMessageGrp::wait_for_write(int node)
|
|---|
| 424 | {
|
|---|
| 425 | semdec.sem_num = node;
|
|---|
| 426 | semop(semid,&semdec,1);
|
|---|
| 427 | semdec.sem_num = 0;
|
|---|
| 428 | }
|
|---|
| 429 |
|
|---|
| 430 | // Release lock for writing to node's buffer.
|
|---|
| 431 | void ShmMessageGrp::release_write(int node)
|
|---|
| 432 | {
|
|---|
| 433 | seminc.sem_num = node;
|
|---|
| 434 | semop(semid,&seminc,1);
|
|---|
| 435 | seminc.sem_num = 0;
|
|---|
| 436 | }
|
|---|
| 437 |
|
|---|
| 438 | #ifdef DEBUG
|
|---|
| 439 | void ShmMessageGrp::print_buffer(int node, int me)
|
|---|
| 440 | {
|
|---|
| 441 | int i;
|
|---|
| 442 | msgbuf_t *message;
|
|---|
| 443 | message = (msgbuf_t*)commbuf[node]->buf;
|
|---|
| 444 |
|
|---|
| 445 | ExEnv::outn() << scprintf("Printing buffer for node %d on node %d\n",node,me);
|
|---|
| 446 | for (i=0; i<commbuf[node]->nmsg; i++) {
|
|---|
| 447 | ExEnv::outn() <<
|
|---|
| 448 | scprintf(" on node %2d: to=%2d, bytes=%6d, type=%10d, from=%2d\n",
|
|---|
| 449 | me,
|
|---|
| 450 | node,
|
|---|
| 451 | message->size,
|
|---|
| 452 | message->type,
|
|---|
| 453 | message->from);
|
|---|
| 454 | ExEnv::outn().flush();
|
|---|
| 455 | message = NEXT_MESSAGE(message);
|
|---|
| 456 | }
|
|---|
| 457 |
|
|---|
| 458 | }
|
|---|
| 459 | #endif
|
|---|
| 460 |
|
|---|
| 461 | /////////////////////////////////////////////////////////////////////////////
|
|---|
| 462 |
|
|---|
| 463 | // Local Variables:
|
|---|
| 464 | // mode: c++
|
|---|
| 465 | // c-file-style: "CLJ"
|
|---|
| 466 | // End:
|
|---|