Don't issue error message if message number in application range.
[wine] / library / port.c
1 /*
2  * Misc. functions for systems that don't have them
3  *
4  * Copyright 1996 Alexandre Julliard
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
21 #include "config.h"
22 #include "wine/port.h"
23
24 #ifdef __BEOS__
25 #include <be/kernel/fs_info.h>
26 #include <be/kernel/OS.h>
27 #endif
28
29 #include <assert.h>
30 #include <ctype.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <sys/types.h>
36 #ifdef HAVE_SYS_INTTYPES_H
37 # include <sys/inttypes.h>
38 #endif
39 #ifdef HAVE_SYS_TIME_h
40 # include <sys/time.h>
41 #endif
42 #include <sys/stat.h>
43 #ifdef HAVE_SYS_IOCTL_H
44 #include <sys/ioctl.h>
45 #endif
46 #include <errno.h>
47 #include <fcntl.h>
48 #ifdef HAVE_TERMIOS_H
49 #include <termios.h>
50 #endif
51 #ifdef HAVE_SYS_MMAN_H
52 #include <sys/mman.h>
53 #endif
54 #ifdef HAVE_LIBIO_H
55 # include <libio.h>
56 #endif
57 #ifdef HAVE_SYSCALL_H
58 # include <syscall.h>
59 #endif
60 #ifdef HAVE_STDINT_H
61 # include <stdint.h>
62 #endif
63
64 /***********************************************************************
65  *              usleep
66  */
67 #ifndef HAVE_USLEEP
68 int usleep (unsigned int useconds)
69 {
70 #if defined(__EMX__)
71     DosSleep(useconds);
72     return 0;
73 #elif defined(__BEOS__)
74     return snooze(useconds);
75 #elif defined(HAVE_SELECT)
76     struct timeval delay;
77
78     delay.tv_sec = useconds / 1000000;
79     delay.tv_usec = useconds % 1000000;
80
81     select( 0, 0, 0, 0, &delay );
82     return 0;
83 #else /* defined(__EMX__) || defined(__BEOS__) || defined(HAVE_SELECT) */
84     errno = ENOSYS;
85     return -1;
86 #endif /* defined(__EMX__) || defined(__BEOS__) || defined(HAVE_SELECT) */
87 }
88 #endif /* HAVE_USLEEP */
89
90 /***********************************************************************
91  *              memmove
92  */
93 #ifndef HAVE_MEMMOVE
94 void *memmove( void *dest, const void *src, unsigned int len )
95 {
96     register char *dst = dest;
97
98     /* Use memcpy if not overlapping */
99     if ((dst + len <= (char *)src) || ((char *)src + len <= dst))
100     {
101         memcpy( dst, src, len );
102     }
103     /* Otherwise do it the hard way (FIXME: could do better than this) */
104     else if (dst < src)
105     {
106         while (len--) *dst++ = *((char *)src)++;
107     }
108     else
109     {
110         dst += len - 1;
111         src = (char *)src + len - 1;
112         while (len--) *dst-- = *((char *)src)--;
113     }
114     return dest;
115 }
116 #endif  /* HAVE_MEMMOVE */
117
118 /***********************************************************************
119  *              strerror
120  */
121 #ifndef HAVE_STRERROR
122 const char *strerror( int err )
123 {
124     /* Let's hope we have sys_errlist then */
125     return sys_errlist[err];
126 }
127 #endif  /* HAVE_STRERROR */
128
129
130 /***********************************************************************
131  *              getpagesize
132  */
133 #ifndef HAVE_GETPAGESIZE
134 size_t getpagesize(void)
135 {
136 # ifdef __svr4__
137     return sysconf(_SC_PAGESIZE);
138 # elif defined(__i386__)
139     return 4096;
140 # else
141 #  error Cannot get the page size on this platform
142 # endif
143 }
144 #endif  /* HAVE_GETPAGESIZE */
145
146
147 /***********************************************************************
148  *              clone
149  */
150 #if !defined(HAVE_CLONE) && defined(__linux__)
151 int clone( int (*fn)(void *), void *stack, int flags, void *arg )
152 {
153 #ifdef __i386__
154     int ret;
155     void **stack_ptr = (void **)stack;
156     *--stack_ptr = arg;  /* Push argument on stack */
157     *--stack_ptr = fn;   /* Push function pointer (popped into ebx) */
158     __asm__ __volatile__( "pushl %%ebx\n\t"
159                           "movl %2,%%ebx\n\t"
160                           "int $0x80\n\t"
161                           "popl %%ebx\n\t"   /* Contains fn in the child */
162                           "testl %%eax,%%eax\n\t"
163                           "jnz 0f\n\t"
164                           "xorl %ebp,%ebp\n\t"    /* Terminate the stack frames */
165                           "call *%%ebx\n\t"       /* Should never return */
166                           "xorl %%eax,%%eax\n\t"  /* Just in case it does*/
167                           "0:"
168                           : "=a" (ret)
169                           : "0" (SYS_clone), "r" (flags), "c" (stack_ptr) );
170     assert( ret );  /* If ret is 0, we returned from the child function */
171     if (ret > 0) return ret;
172     errno = -ret;
173     return -1;
174 #else
175     errno = EINVAL;
176     return -1;
177 #endif  /* __i386__ */
178 }
179 #endif  /* !HAVE_CLONE && __linux__ */
180
181 /***********************************************************************
182  *              strcasecmp
183  */
184 #ifndef HAVE_STRCASECMP
185 int strcasecmp( const char *str1, const char *str2 )
186 {
187     const unsigned char *ustr1 = (const unsigned char *)str1;
188     const unsigned char *ustr2 = (const unsigned char *)str2;
189
190     while (*ustr1 && toupper(*ustr1) == toupper(*ustr2)) {
191         ustr1++;
192         ustr2++;
193     }
194     return toupper(*ustr1) - toupper(*ustr2);
195 }
196 #endif /* HAVE_STRCASECMP */
197
198 /***********************************************************************
199  *              strncasecmp
200  */
201 #ifndef HAVE_STRNCASECMP
202 int strncasecmp( const char *str1, const char *str2, size_t n )
203 {
204     const unsigned char *ustr1 = (const unsigned char *)str1;
205     const unsigned char *ustr2 = (const unsigned char *)str2;
206     int res;
207
208     if (!n) return 0;
209     while ((--n > 0) && *ustr1) {
210         if ((res = toupper(*ustr1) - toupper(*ustr2))) return res;
211         ustr1++;
212         ustr2++;
213     }
214     return toupper(*ustr1) - toupper(*ustr2);
215 }
216 #endif /* HAVE_STRNCASECMP */
217
218 /***********************************************************************
219  *              getnetbyaddr
220  */
221 #ifndef HAVE_GETNETBYADDR
222 struct netent *getnetbyaddr(unsigned long net, int type)
223 {
224     errno = ENOSYS;
225     return NULL;
226 }
227 #endif /* defined(HAVE_GETNETBYNAME) */
228
229 /***********************************************************************
230  *              getnetbyname
231  */
232 #ifndef HAVE_GETNETBYNAME
233 struct netent *getnetbyname(const char *name)
234 {
235     errno = ENOSYS;
236     return NULL;
237 }
238 #endif /* defined(HAVE_GETNETBYNAME) */
239
240 /***********************************************************************
241  *              getprotobyname
242  */
243 #ifndef HAVE_GETPROTOBYNAME
244 struct protoent *getprotobyname(const char *name)
245 {
246     errno = ENOSYS;
247     return NULL;
248 }
249 #endif /* !defined(HAVE_GETPROTOBYNAME) */
250
251 /***********************************************************************
252  *              getprotobynumber
253  */
254 #ifndef HAVE_GETPROTOBYNUMBER
255 struct protoent *getprotobynumber(int proto)
256 {
257     errno = ENOSYS;
258     return NULL;
259 }
260 #endif /* !defined(HAVE_GETPROTOBYNUMBER) */
261
262 /***********************************************************************
263  *              getservbyport
264  */
265 #ifndef HAVE_GETSERVBYPORT
266 struct servent *getservbyport(int port, const char *proto)
267 {
268     errno = ENOSYS;
269     return NULL;
270 }
271 #endif /* !defined(HAVE_GETSERVBYPORT) */
272
273 /***********************************************************************
274  *              getsockopt
275  */
276 #ifndef HAVE_GETSOCKOPT
277 int getsockopt(int socket, int level, int option_name,
278                void *option_value, size_t *option_len)
279 {
280     errno = ENOSYS;
281     return -1;
282 }
283 #endif /* !defined(HAVE_GETSOCKOPT) */
284
285 /***********************************************************************
286  *              inet_network
287  */
288 #ifndef HAVE_INET_NETWORK
289 unsigned long inet_network(const char *cp)
290 {
291     errno = ENOSYS;
292     return 0;
293 }
294 #endif /* defined(HAVE_INET_NETWORK) */
295
296 /***********************************************************************
297  *              statfs
298  */
299 #ifndef HAVE_STATFS
300 int statfs(const char *name, struct statfs *info)
301 {
302 #ifdef __BEOS__
303     dev_t mydev;
304     fs_info fsinfo;
305
306     if(!info) {
307         errno = ENOSYS;
308         return -1;
309     }
310
311     if ((mydev = dev_for_path(name)) < 0) {
312         errno = ENOSYS;
313         return -1;
314     }
315
316     if (fs_stat_dev(mydev,&fsinfo) < 0) {
317         errno = ENOSYS;
318         return -1;
319     }
320
321     info->f_bsize = fsinfo.block_size;
322     info->f_blocks = fsinfo.total_blocks;
323     info->f_bfree = fsinfo.free_blocks;
324     return 0;
325 #else /* defined(__BEOS__) */
326     errno = ENOSYS;
327     return -1;
328 #endif /* defined(__BEOS__) */
329 }
330 #endif /* !defined(HAVE_STATFS) */
331
332
333 /***********************************************************************
334  *              lstat
335  */
336 #ifndef HAVE_LSTAT
337 int lstat(const char *file_name, struct stat *buf)
338 {
339     return stat( file_name, buf );
340 }
341 #endif /* HAVE_LSTAT */
342
343
344 /***********************************************************************
345  *              pread
346  *
347  * FIXME: this is not thread-safe
348  */
349 #ifndef HAVE_PREAD
350 ssize_t pread( int fd, void *buf, size_t count, off_t offset )
351 {
352     ssize_t ret;
353     off_t old_pos;
354
355     if ((old_pos = lseek( fd, 0, SEEK_CUR )) == -1) return -1;
356     if (lseek( fd, offset, SEEK_SET ) == -1) return -1;
357     if ((ret = read( fd, buf, count )) == -1)
358     {
359         int err = errno;  /* save errno */
360         lseek( fd, old_pos, SEEK_SET );
361         errno = err;
362         return -1;
363     }
364     if (lseek( fd, old_pos, SEEK_SET ) == -1) return -1;
365     return ret;
366 }
367 #endif /* HAVE_PREAD */
368
369
370 /***********************************************************************
371  *              pwrite
372  *
373  * FIXME: this is not thread-safe
374  */
375 #ifndef HAVE_PWRITE
376 ssize_t pwrite( int fd, const void *buf, size_t count, off_t offset )
377 {
378     ssize_t ret;
379     off_t old_pos;
380
381     if ((old_pos = lseek( fd, 0, SEEK_CUR )) == -1) return -1;
382     if (lseek( fd, offset, SEEK_SET ) == -1) return -1;
383     if ((ret = write( fd, buf, count )) == -1)
384     {
385         int err = errno;  /* save errno */
386         lseek( fd, old_pos, SEEK_SET );
387         errno = err;
388         return -1;
389     }
390     if (lseek( fd, old_pos, SEEK_SET ) == -1) return -1;
391     return ret;
392 }
393 #endif /* HAVE_PWRITE */
394
395
396 #if defined(__svr4__) || defined(__NetBSD__)
397 /***********************************************************************
398  *             try_mmap_fixed
399  *
400  * The purpose of this routine is to emulate the behaviour of
401  * the Linux mmap() routine if a non-NULL address is passed,
402  * but the MAP_FIXED flag is not set.  Linux in this case tries
403  * to place the mapping at the specified address, *unless* the
404  * range is already in use.  Solaris, however, completely ignores
405  * the address argument in this case.
406  *
407  * As Wine code occasionally relies on the Linux behaviour, e.g. to
408  * be able to map non-relocateable PE executables to their proper
409  * start addresses, or to map the DOS memory to 0, this routine
410  * emulates the Linux behaviour by checking whether the desired
411  * address range is still available, and placing the mapping there
412  * using MAP_FIXED if so.
413  */
414 static int try_mmap_fixed (void *addr, size_t len, int prot, int flags,
415                            int fildes, off_t off)
416 {
417     char * volatile result = NULL;
418     int pagesize = getpagesize();
419     pid_t pid;
420
421     /* We only try to map to a fixed address if
422        addr is non-NULL and properly aligned,
423        and MAP_FIXED isn't already specified. */
424
425     if ( !addr )
426         return 0;
427     if ( (uintptr_t)addr & (pagesize-1) )
428         return 0;
429     if ( flags & MAP_FIXED )
430         return 0;
431
432     /* We use vfork() to freeze all threads of the
433        current process.  This allows us to check without
434        race condition whether the desired memory range is
435        already in use.  Note that because vfork() shares
436        the address spaces between parent and child, we
437        can actually perform the mapping in the child. */
438
439     if ( (pid = vfork()) == -1 )
440     {
441         perror("try_mmap_fixed: vfork");
442         exit(1);
443     }
444     if ( pid == 0 )
445     {
446         int i;
447         char vec;
448
449         /* We call mincore() for every page in the desired range.
450            If any of these calls succeeds, the page is already
451            mapped and we must fail. */
452         for ( i = 0; i < len; i += pagesize )
453             if ( mincore( (caddr_t)addr + i, pagesize, &vec ) != -1 )
454                _exit(1);
455
456         /* Perform the mapping with MAP_FIXED set.  This is safe
457            now, as none of the pages is currently in use. */
458         result = mmap( addr, len, prot, flags | MAP_FIXED, fildes, off );
459         if ( result == addr )
460             _exit(0);
461
462         if ( result != (void *) -1 ) /* This should never happen ... */
463             munmap( result, len );
464
465        _exit(1);
466     }
467
468     /* vfork() lets the parent continue only after the child
469        has exited.  Furthermore, Wine sets SIGCHLD to SIG_IGN,
470        so we don't need to wait for the child. */
471
472     return result == addr;
473 }
474 #endif
475
476 /***********************************************************************
477  *              wine_anon_mmap
478  *
479  * Portable wrapper for anonymous mmaps
480  */
481 void *wine_anon_mmap( void *start, size_t size, int prot, int flags )
482 {
483 #ifdef HAVE_MMAP
484     static int fdzero = -1;
485
486 #ifdef MAP_ANON
487     flags |= MAP_ANON;
488 #else
489     if (fdzero == -1)
490     {
491         if ((fdzero = open( "/dev/zero", O_RDONLY )) == -1)
492         {
493             perror( "/dev/zero: open" );
494             exit(1);
495         }
496     }
497 #endif  /* MAP_ANON */
498
499 #ifdef MAP_SHARED
500     flags &= ~MAP_SHARED;
501 #endif
502
503     /* Linux EINVAL's on us if we don't pass MAP_PRIVATE to an anon mmap */
504 #ifdef MAP_PRIVATE
505     flags |= MAP_PRIVATE;
506 #endif
507
508 #if defined(__svr4__) || defined(__NetBSD__)
509     if ( try_mmap_fixed( start, size, prot, flags, fdzero, 0 ) )
510         return start;
511 #endif
512
513     return mmap( start, size, prot, flags, fdzero, 0 );
514 #else
515     return (void *)-1;
516 #endif
517 }
518
519
520 /*
521  * These functions provide wrappers around dlopen() and associated
522  * functions.  They work around a bug in glibc 2.1.x where calling
523  * a dl*() function after a previous dl*() function has failed
524  * without a dlerror() call between the two will cause a crash.
525  * They all take a pointer to a buffer that
526  * will receive the error description (from dlerror()).  This
527  * parameter may be NULL if the error description is not required.
528  */
529
530 /***********************************************************************
531  *              wine_dlopen
532  */
533 void *wine_dlopen( const char *filename, int flag, char *error, int errorsize )
534 {
535 #ifdef HAVE_DLOPEN
536     void *ret;
537     const char *s;
538     dlerror(); dlerror();
539     ret = dlopen( filename, flag );
540     s = dlerror();
541     if (error)
542     {
543         strncpy( error, s ? s : "", errorsize );
544         error[errorsize - 1] = '\0';
545     }
546     dlerror();
547     return ret;
548 #else
549     if (error)
550     {
551         strncpy( error, "dlopen interface not detected by configure", errorsize );
552         error[errorsize - 1] = '\0';
553     }
554     return NULL;
555 #endif
556 }
557
558 /***********************************************************************
559  *              wine_dlsym
560  */
561 void *wine_dlsym( void *handle, const char *symbol, char *error, int errorsize )
562 {
563 #ifdef HAVE_DLOPEN
564     void *ret;
565     const char *s;
566     dlerror(); dlerror();
567     ret = dlsym( handle, symbol );
568     s = dlerror();
569     if (error)
570     {
571         strncpy( error, s ? s : "", errorsize );
572         error[errorsize - 1] = '\0';
573     }
574     dlerror();
575     return ret;
576 #else
577     if (error)
578     {
579         strncpy( error, "dlopen interface not detected by configure", errorsize );
580         error[errorsize - 1] = '\0';
581     }
582     return NULL;
583 #endif
584 }
585
586 /***********************************************************************
587  *              wine_dlclose
588  */
589 int wine_dlclose( void *handle, char *error, int errorsize )
590 {
591 #ifdef HAVE_DLOPEN
592     int ret;
593     const char *s;
594     dlerror(); dlerror();
595     ret = dlclose( handle );
596     s = dlerror();
597     if (error)
598     {
599         strncpy( error, s ? s : "", errorsize );
600         error[errorsize - 1] = '\0';
601     }
602     dlerror();
603     return ret;
604 #else
605     if (error)
606     {
607         strncpy( error, "dlopen interface not detected by configure", errorsize );
608         error[errorsize - 1] = '\0';
609     }
610     return 1;
611 #endif
612 }
613
614 /***********************************************************************
615  *              wine_rewrite_s4tos2
616  *
617  * Convert 4 byte Unicode strings to 2 byte Unicode strings in-place.
618  * This is only practical if literal strings are writable.
619  */
620 unsigned short* wine_rewrite_s4tos2(const wchar_t* str4 )
621 {
622     unsigned short *str2,*s2;
623
624     if (str4==NULL)
625       return NULL;
626
627     if ((*str4 & 0xffff0000) != 0) {
628         /* This string has already been converted. Return it as is */
629         return (unsigned short*)str4;
630     }
631
632     /* Note that we can also end up here if the string has a single
633      * character. In such a case we will convert the string over and
634      * over again. But this is harmless.
635      */
636     str2=s2=(unsigned short*)str4;
637     do {
638         *s2=(unsigned short)*str4;
639         s2++;
640     } while (*str4++ != L'\0');
641
642     return str2;
643 }
644
645 #ifndef HAVE_ECVT
646 /*
647  * NetBSD 1.5 doesn't have ecvt, fcvt, gcvt. We just check for ecvt, though.
648  * Fix/verify these implementations !
649  */
650
651 /***********************************************************************
652  *              ecvt
653  */
654 char *ecvt (double number, int  ndigits,  int  *decpt,  int *sign)
655 {
656     static char buf[40]; /* ought to be enough */
657     char *dec;
658     sprintf(buf, "%.*e", ndigits /* FIXME wrong */, number);
659     *sign = (number < 0);
660     dec = strchr(buf, '.');
661     *decpt = (dec) ? (int)dec - (int)buf : -1;
662     return buf;
663 }
664
665 /***********************************************************************
666  *              fcvt
667  */
668 char *fcvt (double number, int  ndigits,  int  *decpt,  int *sign)
669 {
670     static char buf[40]; /* ought to be enough */
671     char *dec;
672     sprintf(buf, "%.*e", ndigits, number);
673     *sign = (number < 0);
674     dec = strchr(buf, '.');
675     *decpt = (dec) ? (int)dec - (int)buf : -1;
676     return buf;
677 }
678
679 /***********************************************************************
680  *              gcvt
681  *
682  * FIXME: uses both E and F.
683  */
684 char *gcvt (double number, size_t  ndigit,  char *buff)
685 {
686     sprintf(buff, "%.*E", (int)ndigit, number);
687     return buff;
688 }
689 #endif /* HAVE_ECVT */
690
691
692 /***********************************************************************
693  *              interlocked functions
694  */
695 #ifdef __i386__
696
697 __ASM_GLOBAL_FUNC(interlocked_cmpxchg,
698                   "movl 12(%esp),%eax\n\t"
699                   "movl 8(%esp),%ecx\n\t"
700                   "movl 4(%esp),%edx\n\t"
701                   "lock; cmpxchgl %ecx,(%edx)\n\t"
702                   "ret");
703 __ASM_GLOBAL_FUNC(interlocked_cmpxchg_ptr,
704                   "movl 12(%esp),%eax\n\t"
705                   "movl 8(%esp),%ecx\n\t"
706                   "movl 4(%esp),%edx\n\t"
707                   "lock; cmpxchgl %ecx,(%edx)\n\t"
708                   "ret");
709 __ASM_GLOBAL_FUNC(interlocked_xchg,
710                   "movl 8(%esp),%eax\n\t"
711                   "movl 4(%esp),%edx\n\t"
712                   "lock; xchgl %eax,(%edx)\n\t"
713                   "ret");
714 __ASM_GLOBAL_FUNC(interlocked_xchg_ptr,
715                   "movl 8(%esp),%eax\n\t"
716                   "movl 4(%esp),%edx\n\t"
717                   "lock; xchgl %eax,(%edx)\n\t"
718                   "ret");
719 __ASM_GLOBAL_FUNC(interlocked_xchg_add,
720                   "movl 8(%esp),%eax\n\t"
721                   "movl 4(%esp),%edx\n\t"
722                   "lock; xaddl %eax,(%edx)\n\t"
723                   "ret");
724
725 #elif defined(__sparc__) && defined(__sun__)
726
727 /*
728  * As the earlier Sparc processors lack necessary atomic instructions,
729  * I'm simply falling back to the library-provided _lwp_mutex routines
730  * to ensure mutual exclusion in a way appropriate for the current
731  * architecture.
732  *
733  * FIXME:  If we have the compare-and-swap instruction (Sparc v9 and above)
734  *         we could use this to speed up the Interlocked operations ...
735  */
736 #include <synch.h>
737 static lwp_mutex_t interlocked_mutex = DEFAULTMUTEX;
738
739 long interlocked_cmpxchg( long *dest, long xchg, long compare )
740 {
741     _lwp_mutex_lock( &interlocked_mutex );
742     if (*dest == compare) *dest = xchg;
743     else compare = *dest;
744     _lwp_mutex_unlock( &interlocked_mutex );
745     return compare;
746 }
747
748 void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare )
749 {
750     _lwp_mutex_lock( &interlocked_mutex );
751     if (*dest == compare) *dest = xchg;
752     else compare = *dest;
753     _lwp_mutex_unlock( &interlocked_mutex );
754     return compare;
755 }
756
757 long interlocked_xchg( long *dest, long val )
758 {
759     long retv;
760     _lwp_mutex_lock( &interlocked_mutex );
761     retv = *dest;
762     *dest = val;
763     _lwp_mutex_unlock( &interlocked_mutex );
764     return retv;
765 }
766
767 void *interlocked_xchg_ptr( void **dest, void *val )
768 {
769     long retv;
770     _lwp_mutex_lock( &interlocked_mutex );
771     retv = *dest;
772     *dest = val;
773     _lwp_mutex_unlock( &interlocked_mutex );
774     return retv;
775 }
776
777 long interlocked_xchg_add( long *dest, long incr )
778 {
779     long retv;
780     _lwp_mutex_lock( &interlocked_mutex );
781     retv = *dest;
782     *dest += incr;
783     _lwp_mutex_unlock( &interlocked_mutex );
784     return retv;
785 }
786
787 #else
788 # error You must implement the interlocked* functions for your CPU
789 #endif