1 /*
2 * ***************************************************************************
3 * MALOC = < Minimal Abstraction Layer for Object-oriented C >
4 * Copyright (C) 1994-- Michael Holst
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 *
20 * rcsid="$Id: vcom.c,v 1.25 2010/08/12 05:40:21 fetk Exp $"
21 * ***************************************************************************
22 */
23
24 /*
25 * ***************************************************************************
26 * File: vcom.c
27 *
28 * Purpose: Class Vcom: methods.
29 *
30 * Author: Nathan Baker and Michael Holst
31 * ***************************************************************************
32 */
33
34 #include "vcom_p.h"
35
36 VEMBED(rcsid="$Id: vcom.c,v 1.25 2010/08/12 05:40:21 fetk Exp $")
37
38 /*
39 * ***************************************************************************
40 * Class Vcom: Inlineable methods
41 * ***************************************************************************
42 */
43 #if !defined(VINLINE_MALOC)
44
45 #endif /* if !defined(VINLINE_MALOC) */
46
47 /*
48 * ***************************************************************************
49 * Class Vcom: Non-inlineable methods
50 * ***************************************************************************
51 */
52
53 /*
54 * ***************************************************************************
55 * Routine: Vcom_init
56 *
57 * Purpose: The Vmp initializer.
58 *
59 * Author: Michael Holst
60 * ***************************************************************************
61 */
Vcom_init(int * argc,char *** argv)62 VPUBLIC int Vcom_init(int *argc, char ***argv)
63 {
64 #if defined(HAVE_MPI_H)
65 return (MPI_SUCCESS == MPI_Init(argc,argv));
66 #else
67 return 1;
68 #endif
69 }
70
71 /*
72 * ***************************************************************************
73 * Routine: Vcom_finalize
74 *
75 * Purpose: The Vmp finalizerr.
76 *
77 * Author: Michael Holst
78 * ***************************************************************************
79 */
Vcom_finalize(void)80 VPUBLIC int Vcom_finalize(void)
81 {
82 #if defined(HAVE_MPI_H)
83 return (MPI_SUCCESS == MPI_Finalize());
84 #else
85 return 1;
86 #endif
87 }
88
89 /*
90 * ***************************************************************************
91 * Routine: Vcom_ctor
92 *
93 * Purpose: Construct the communications object
94 *
95 * Notes: This routine sets up data members of class and initializes MPI.
96 *
97 * Author: Nathan Baker and Michael Holst
98 * ***************************************************************************
99 */
Vcom_ctor(int commtype)100 VPUBLIC Vcom* Vcom_ctor(int commtype)
101 {
102 int rc;
103 Vcom *thee = VNULL;
104
105 /* Set up the structure */
106 thee = Vmem_malloc( VNULL, 1, sizeof(Vcom) );
107 thee->core = Vmem_malloc( VNULL, 1, sizeof(Vcom_core) );
108
109 /* Call the real constructor */
110 rc = Vcom_ctor2(thee, commtype);
111
112 /* Destroy the guy if something went wrong */
113 if (rc == 0) {
114 Vmem_free( VNULL, 1, sizeof(Vcom_core), (void**)&(thee->core) );
115 Vmem_free( VNULL, 1, sizeof(Vcom), (void**)&thee );
116 }
117
118 return thee;
119 }
120
121 /*
122 * ***************************************************************************
123 * Routine: Vcom_ctor2
124 *
125 * Purpose: Construct the communications object
126 *
127 * Notes: This routine sets up data members of class and initializes MPI.
128 *
129 * This is broken into two parts to be callable from FORTRAN.
130 *
131 * Author: Nathan Baker and Michael Holst
132 * ***************************************************************************
133 */
Vcom_ctor2(Vcom * thee,int commtype)134 VPUBLIC int Vcom_ctor2(Vcom *thee, int commtype)
135 {
136 int rc = 0;
137
138 #if defined(HAVE_MPI_H)
139 char estr[MPI_MAX_ERROR_STRING];
140 int elen, dummy;
141 Vcom_core *core = thee->core;
142 #endif
143
144 /*
145 * See what type of communications we should use. Maybe each type of
146 * communications object should have its own ctor2() function.
147 */
148 switch ( commtype ) {
149 case 1: /* MPI 1.1 */
150 thee->type = commtype;
151
152 #if defined(HAVE_MPI_H)
153 /* Start up MPI */
154 rc = MPI_Initialized(&dummy);
155 if (rc != MPI_SUCCESS) {
156 MPI_Error_string(rc, estr, &elen);
157 Vnm_print(2, "Vcom_ctor2: MPI_Init returned error: %s\n",
158 estr);
159 return 0;
160 }
161
162 /* Get the total number of processors */
163 rc = MPI_Comm_size(MPI_COMM_WORLD, &(thee->mpi_size));
164 if (rc != MPI_SUCCESS) {
165 MPI_Error_string(rc, estr, &elen);
166 Vnm_print(2, "Vcom_ctor2: MPI_Comm_size returned error: %s\n",
167 estr);
168 return 0;
169 }
170
171 /* Get my processor rank */
172 rc = MPI_Comm_rank(MPI_COMM_WORLD, &(thee->mpi_rank));
173 if (rc != MPI_SUCCESS) {
174 MPI_Error_string(rc, estr, &elen);
175 Vnm_print(2, "Vcom_ctor2: MPI_Comm_rank returned error: %s\n",
176 estr);
177 return 0;
178 }
179
180 /* Construct the communications group including all processors */
181 core->mpi_comm = MPI_COMM_WORLD;
182
183 /* Initialize Vnm with MPI rank */
184 Vnm_setIoTag(thee->mpi_rank, thee->mpi_size);
185
186 /* Some i/o */
187 Vnm_print(2,"Vcom_ctor2: process %d of %d is ALIVE!\n",
188 thee->mpi_rank, thee->mpi_size);
189
190 rc = 1;
191 break;
192
193 #else /* defined(HAVE_MPI_H) */
194
195 /* this might not be an error if this is a sequential code... */
196 rc = 1;
197 break;
198
199 #endif /* defined(HAVE_MPI_H) */
200
201 default:
202 Vnm_print(2, "Vcom_ctor2: Invalid communications type!\n");
203 rc = 0;
204
205 } /* switch (commtype) */
206
207 return rc;
208 }
209
210 /*
211 * ***************************************************************************
212 * Routine: Vcom_resize
213 *
214 * Purpose: Resize (shrink) the communications group to include only newsize
215 * number of processors
216 *
217 * Notes: Obsolete processes are given rank of -1 and size of 0
218 *
219 * Returns: 1 if sucessful
220 *
221 * Author: Nathan Baker
222 * ***************************************************************************
223 */
Vcom_resize(Vcom * thee,int newsize)224 VPUBLIC int Vcom_resize(Vcom *thee, int newsize)
225 {
226 #if defined(HAVE_MPI_H)
227 int color;
228 MPI_Comm oldcomm;
229 Vcom_core *core = thee->core;
230 #endif
231
232 switch (thee->type) {
233 case 1: /* MPI 1.1 */
234 #if defined(HAVE_MPI_H)
235 /* This is a no-op for obsolete processes */
236 if (core->mpi_comm == MPI_COMM_NULL) return 1;
237 Vcom_barr(thee);
238 /* Split the communications group. We will ignore all processes
239 * with rank outside the desired size */
240 if (newsize > thee->mpi_size) {
241 Vnm_print(2, "Vcom_resize: Requested number of processors (%d) greater than original size (%d)!\n", newsize, thee->mpi_size);
242 return 0;
243 }
244 if (thee->mpi_rank < newsize) color = 0;
245 else color = MPI_UNDEFINED;
246 MPI_Comm_dup(core->mpi_comm, &oldcomm);
247 if (MPI_Comm_split(oldcomm, color, 0, &(core->mpi_comm))
248 != MPI_SUCCESS) {
249 Vnm_print(2, "Vcom_resize: Failed to split communicator!\n");
250 return 0;
251 }
252 MPI_Comm_free(&oldcomm);
253 if (core->mpi_comm != MPI_COMM_NULL) {
254 MPI_Comm_rank(core->mpi_comm, &(thee->mpi_rank));
255 MPI_Comm_size(core->mpi_comm, &(thee->mpi_size));
256 } else {
257 thee->mpi_rank = -1;
258 thee->mpi_size = 0;
259 }
260 Vnm_print(0, "Vcom_resize: New comm size = %d\n", thee->mpi_size);
261 return 1;
262 #else
263 Vnm_print(2, "Vcom_resize: Not compiled with MPI!\n");
264 return 0;
265 #endif
266 break;
267 default:
268 Vnm_print(2, "Vcom_resize: Invalid communications type!\n");
269 return 0;
270 }
271 }
272
273 /*
274 * ***************************************************************************
275 * Routine: Vcom_dtor
276 *
277 * Purpose: Destroy the communications object
278 *
279 * Author: Nathan Baker and Michael Holst
280 * ***************************************************************************
281 */
Vcom_dtor(Vcom ** thee)282 VPUBLIC void Vcom_dtor(Vcom **thee)
283 {
284 if ((*thee) != VNULL) {
285 Vcom_dtor2(*thee);
286 Vmem_free( VNULL, 1, sizeof(Vcom_core), (void**)&((*thee)->core) );
287 Vmem_free( VNULL, 1, sizeof(Vcom), (void**)thee );
288 }
289 }
290
291 /*
292 * ***************************************************************************
293 * Routine: Vcom_dtor2
294 *
295 * Purpose: Destroy the communications object
296 *
297 * Notes: This is broken into two parts to be callable from FORTRAN.
298 *
299 * Author: Nathan Baker and Michael Holst
300 * ***************************************************************************
301 */
Vcom_dtor2(Vcom * thee)302 VPUBLIC void Vcom_dtor2(Vcom *thee)
303 {
304 #if defined(HAVE_MPI_H)
305 int err;
306 Vcom_core *core = thee->core;
307 #endif
308
309 /*
310 * Do various things depending on what type of communications object
311 * this is. Maybe each object type should have its own dtor2() function.
312 */
313 switch (thee->type) {
314
315 case 1: /* MPI 1.1 */
316 #if defined(HAVE_MPI_H)
317 /* Destroy the communicator */
318 if ((core->mpi_comm != MPI_COMM_NULL) &&
319 (core->mpi_comm != MPI_COMM_WORLD)) {
320 Vnm_print(0, "Vcom_dtor2: Freeing MPI communicator...\n");
321 MPI_Comm_free(&(core->mpi_comm));
322 }
323
324 #if 0
325 err = MPI_Finalize();
326 if (err != MPI_SUCCESS) {
327 Vnm_print(2, "Vcom_dtor2: MPI_Finalize returned %d\n", err);
328 }
329 #endif
330 #endif
331 default:
332 return;
333 }
334 }
335
336 /*
337 * ***************************************************************************
338 * Routine: Vcom_send
339 *
340 * Purpose: Send a buffer. Returns 1 on success.
341 *
342 * Args: des = rank of receiving processor
343 * buf = buffer containing message
344 * len = number of items (of declared type) in buffer
345 * type = type of items in message
346 * 0 => MPI_BYTE
347 * 1 => MPI_INT
348 * 2 => MPI_DOUBLE
349 * 3 => MPI_CHAR
350 * block = toggles blocking on (=1) and off (=0)
351 *
352 * Returns: 1 if successful
353 *
354 * Author: Nathan Baker and Michael Holst
355 * ***************************************************************************
356 */
Vcom_send(Vcom * thee,int des,void * buf,int len,int type,int block)357 VPUBLIC int Vcom_send(Vcom *thee, int des, void *buf, int len, int type,
358 int block)
359 {
360 #if defined(HAVE_MPI_H)
361 int tag = VCOM_MPI_TAG; /* MPI tag */
362 MPI_Datatype datatype;
363 Vcom_core *core = thee->core;
364 #endif
365
366 int err = 1; /* Error flag (success = 1) */
367
368 /* Bail if we've received any errors */
369 VASSERT(thee != VNULL);
370
371 if (thee->error != 0) {
372 Vnm_print(2, "Vcom_send: Have non-zero error state (%d)!\n",
373 thee->error);
374 return 0;
375 }
376
377 /* Figure out data type to use */
378 #if defined(HAVE_MPI_H)
379 switch(type) {
380 case 0:
381 datatype = MPI_BYTE;
382 break;
383 case 1:
384 datatype = MPI_INT;
385 break;
386 case 2:
387 datatype = MPI_DOUBLE;
388 break;
389 case 3:
390 datatype = MPI_CHAR;
391 break;
392 default:
393 Vnm_print(2, "Vcom_send: Bogus datatype (%d), bailing!\n", type);
394 return 0;
395 }
396 #endif
397
398 /* Send routine depends on comm object type */
399 switch(thee->type) {
400
401 case 1: /* MPI 1.1 */
402 #if defined(HAVE_MPI_H)
403 if (core->mpi_comm == MPI_COMM_NULL) return 1;
404 /* To block or not to block... */
405 if (block == 1) {
406 err = MPI_Send(buf, len, datatype, des, tag, core->mpi_comm);
407 err = (MPI_SUCCESS == err);
408 } else { /* if (block == 1) */
409 err = MPI_Isend(buf, len, datatype, des, tag, core->mpi_comm,
410 &(core->mpi_request));
411 err = (MPI_SUCCESS == err);
412 } /* if (block == 1) */
413 #else
414 Vnm_print(2, "Vcom_send: Vcom not compiled with MPI!\n");
415 return 0;
416 #endif
417 break;
418 default:
419 Vnm_print(2, "Vcom_send: Invalid communications type!\n");
420 return 0;
421 }
422
423 return err;
424 }
425
426 /*
427 * ***************************************************************************
428 * Routine: Vcom_recv
429 *
430 * Purpose: Receive a (character) buffer. Returns 1 on success.
431 *
432 * Args: src = rank of sending processor
433 * buf = pointer to buffer of previously allocated memory
434 * len = number of items (of declared type) in buffer
435 * type = type of items in message
436 * 0 => MPI_BYTE
437 * 1 => MPI_INT
438 * 2 => MPI_DOUBLE
439 * 3 => MPI_CHAR
440 * block = toggles blocking on (=1) and off (=0)
441 *
442 * Returns: 1 if successful
443 *
444 * Notes: The blocking flag is present, but not used. All receives are
445 * assumed to be blocking. A non-blocking receive would be *very*
446 * ugly to implement (signals or something?).
447 *
448 * Author: Nathan Baker and Michael Holst
449 * ***************************************************************************
450 */
Vcom_recv(Vcom * thee,int src,void * buf,int len,int type,int block)451 VPUBLIC int Vcom_recv(Vcom *thee, int src, void *buf, int len, int type,
452 int block)
453 {
454 int err = 0; /* Error flag (success = 1) */
455
456 #if defined(HAVE_MPI_H)
457 int tag = VCOM_MPI_TAG; /* MPI tag */
458 MPI_Datatype datatype;
459 Vcom_core *core = thee->core;
460 #endif
461
462 /* Bail if we've received any errors */
463 VASSERT(thee != VNULL);
464 if (thee->error != 0) {
465 Vnm_print(2, "Vcom_send: Have non-zero error state (%d)!\n",
466 thee->error);
467 return 0;
468 }
469
470
471 #if defined(HAVE_MPI_H)
472 switch(type) {
473 case 0:
474 datatype = MPI_BYTE;
475 break;
476 case 1:
477 datatype = MPI_INT;
478 break;
479 case 2:
480 datatype = MPI_DOUBLE;
481 break;
482 case 3:
483 datatype = MPI_CHAR;
484 break;
485 default:
486 Vnm_print(2, "Vcom_recv: Bogus datatype (%d), bailing!\n", type);
487 return 0;
488 }
489 #endif
490
491 /* Send routine depends on comm object type */
492 switch(thee->type) {
493
494 case 1: /* MPI 1.1 */
495 #if defined(HAVE_MPI_H)
496 if (core->mpi_comm == MPI_COMM_NULL) return 1;
497 /* To block or not to block... */
498 if (block == 1) {
499 err = MPI_Recv(buf, len, datatype, src, tag,
500 core->mpi_comm, &(core->mpi_status));
501 err = (MPI_SUCCESS == err);
502 } else {
503 Vnm_print(2, "Vcom_recv: WARNING! Non-blocking receive not implemented!\n");
504 return 0;
505 }
506 #else
507 Vnm_print(2, "Vcom_recv: Vcom not compiled with MPI!\n");
508 return 0;
509 #endif
510
511 break;
512 default:
513 Vnm_print(2, "Vcom_recv: Invalid communications type!\n");
514 return 0;
515 }
516 return err;
517 }
518
519 /*
520 * ***************************************************************************
521 * Routine: Vcom_size
522 *
523 * Purpose: Get the number of PEs in communicator
524 *
525 * Returns: Number of PEs or -1 if error
526 *
527 * Author: Nathan Baker and Michael Holst
528 * ***************************************************************************
529 */
Vcom_size(Vcom * thee)530 VPUBLIC int Vcom_size(Vcom *thee)
531 {
532 #if defined(HAVE_MPI_H)
533 Vcom_core *core = thee->core;
534 #endif
535
536 VASSERT(thee != VNULL);
537
538 if ( thee->type == 1) {
539 #if defined(HAVE_MPI_H)
540 if (core->mpi_comm == MPI_COMM_NULL) return 0;
541 return thee->mpi_size;
542 #else
543 return 1;
544 #endif
545 } else { return -1; }
546 }
547
548 /*
549 * ***************************************************************************
550 * Routine: Vcom_rank
551 *
552 * Purpose: Get the ID of the local PE
553 *
554 * Returns: PE rank or -1 if error
555 *
556 * Author: Nathan Baker and Michael Holst
557 * ***************************************************************************
558 */
Vcom_rank(Vcom * thee)559 VPUBLIC int Vcom_rank(Vcom *thee)
560 {
561 #if defined(HAVE_MPI_H)
562 Vcom_core *core = thee->core;
563 #endif
564
565 VASSERT(thee != VNULL);
566
567 if ( thee->type == 1) {
568 #if defined(HAVE_MPI_H)
569 if (core->mpi_comm == MPI_COMM_NULL) return -1;
570 return thee->mpi_rank;
571 #else
572 return 0;
573 #endif
574 } else { return -1; }
575 }
576
577 /*
578 * ***************************************************************************
579 * Routine: Vcom_barr
580 *
581 * Purpose: Synchronization barrier.
582 *
583 * Returns: 1 if succesful
584 *
585 * Author: Michael Holst
586 * ***************************************************************************
587 */
Vcom_barr(Vcom * thee)588 VPUBLIC int Vcom_barr(Vcom *thee)
589 {
590 #if defined(HAVE_MPI_H)
591 int err;
592 Vcom_core *core = thee->core;
593 #endif
594
595 VASSERT(thee != VNULL);
596
597 if ( thee->type == 1) {
598
599 #if defined(HAVE_MPI_H)
600 if (core->mpi_comm != MPI_COMM_NULL) {
601 err = MPI_Barrier(core->mpi_comm);
602 return (err == MPI_SUCCESS);
603 } else return 1;
604 #else
605 Vnm_print(2, "Vcom_barr: Vcom not compiled with MPI!\n");
606 return 0;
607 #endif
608 } else {
609 Vnm_print(2, "Vcom_barr: Invalid communications type!\n");
610 return 0;
611 }
612 return 0;
613 }
614
615 /*
616 * ***************************************************************************
617 * Routine: Vcom_getCount
618 *
619 * Purpose: Perform a blocking probe to get the length (in number of items of
620 * specified type) of an incoming message and place it in the
621 * argument ``length".
622 *
623 * type = type of items in message
624 * 0 => MPI_BYTE
625 * 1 => MPI_INT
626 * 2 => MPI_DOUBLE
627 * 3 => MPI_CHAR
628 *
629 * Author: Nathan Baker
630 * ***************************************************************************
631 */
Vcom_getCount(Vcom * thee,int src,int * length,int type)632 VPUBLIC int Vcom_getCount(Vcom *thee, int src, int *length, int type)
633 {
634 #if defined(HAVE_MPI_H)
635 MPI_Datatype datatype;
636 Vcom_core *core = thee->core;
637 #endif
638
639 VASSERT(thee != VNULL);
640
641 #if defined(HAVE_MPI_H)
642 switch(type) {
643 case 0:
644 datatype = MPI_BYTE;
645 break;
646 case 1:
647 datatype = MPI_INT;
648 break;
649 case 2:
650 datatype = MPI_DOUBLE;
651 break;
652 case 3:
653 datatype = MPI_CHAR;
654 break;
655 default:
656 Vnm_print(2,"Vcom_getCount: Bogus datatype (%d), bailing!\n",type);
657 return 0;
658 }
659 #endif
660
661 if ( thee->type == 1) {
662 #if defined(HAVE_MPI_H)
663 if (core->mpi_comm == MPI_COMM_NULL) return 1;
664 MPI_Probe(src, VCOM_MPI_TAG, core->mpi_comm, &(core->mpi_status));
665 return MPI_Get_count(&(core->mpi_status), datatype, length);
666 #else
667 Vnm_print(2, "Vcom_getCount: Vcom not compiled with MPI!\n");
668 return -1;
669 #endif
670 } else { return -1; }
671 }
672
673 /*
674 * ***************************************************************************
675 * Routine: Vcom_reduce
676 *
677 * Purpose: Perform a reduction of the data across all processors. This is
678 * equivalent (and in the case of MPI is identical to) MPI_Allreduce.
679 * Basically, the specified operations are appleed to each member of
680 * the sendbuf across all processors and the results are written to
681 * recvbuf.
682 *
683 * Args: sendbuf = buffer containing `length` items of the specified type
684 * to be operated on
685 * recvbuf = buffer containing `length` items of the specified type
686 * after operation
687 * length = number of items
688 * type = type of items in message
689 * 0 => MPI_BYTE
690 * 1 => MPI_INT
691 * 2 => MPI_DOUBLE
692 * 3 => MPI_CHAR
693 * op = operation to perform
694 * 0 => MPI_SUM
695 * 1 => MPI_PROD
696 * 2 => MPI_MIN
697 * 3 => MPI_MAX
698 *
699 * Author: Nathan Baker
700 * ***************************************************************************
701 */
Vcom_reduce(Vcom * thee,void * sendbuf,void * recvbuf,int length,int type,int op)702 VPUBLIC int Vcom_reduce(Vcom *thee, void *sendbuf, void *recvbuf, int length,
703 int type, int op)
704 {
705 int memsize;
706
707 #if defined(HAVE_MPI_H)
708 MPI_Datatype datatype;
709 MPI_Op optype;
710 Vcom_core *core = thee->core;
711 #endif
712
713 VASSERT(thee != VNULL);
714
715 #if defined(HAVE_MPI_H)
716 switch(type) {
717 case 0:
718 datatype = MPI_BYTE;
719 break;
720 case 1:
721 datatype = MPI_INT;
722 break;
723 case 2:
724 datatype = MPI_DOUBLE;
725 break;
726 case 3:
727 datatype = MPI_CHAR;
728 break;
729 default:
730 Vnm_print(2, "Vcom_recv: Bogus datatype (%d), bailing!\n", type);
731 return 0;
732 }
733 switch(op) {
734 case 0:
735 optype = MPI_SUM;
736 break;
737 case 1:
738 optype = MPI_PROD;
739 break;
740 case 2:
741 optype = MPI_MIN;
742 break;
743 case 3:
744 optype = MPI_MAX;
745 break;
746 default:
747 Vnm_print(2, "Vcom_reduce: Bogus optype (%d), bailing!\n", type);
748 return 0;
749 }
750 #endif
751
752 if ( thee->type == 1) {
753
754 #if defined(HAVE_MPI_H)
755
756 if (core->mpi_comm == MPI_COMM_NULL) return 1;
757 return MPI_Allreduce(sendbuf, recvbuf, length, datatype, optype,
758 core->mpi_comm);
759
760 #else
761 Vnm_print(0, "Vcom_reduce: Not compiled with MPI, doing simple copy.\n");
762 switch(type) {
763 case 0:
764 memsize = 1;
765 break;
766 case 1:
767 memsize = sizeof(int);
768 break;
769 case 2:
770 memsize = sizeof(double);
771 break;
772 case 3:
773 memsize = sizeof(char);
774 break;
775 default:
776 Vnm_print(2, "Vcom_recv: Bogus datatype (%d), bailing!\n", type);
777 return 0;
778 }
779
780 memcpy(recvbuf, sendbuf, memsize*length);
781 return 1;
782
783 #endif
784
785 } else { return -1; }
786 }
787
788