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