In the latest sdk the {waveOut,midiOut}{Get,Set}Volume functions take
[wine/multimedia.git] / library / port.c
blob8033934694faa2df48c7386142bd804f3455f6b7
1 /*
2 * Misc. functions for systems that don't have them
4 * Copyright 1996 Alexandre Julliard
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.
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.
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
21 #include "config.h"
22 #include "wine/port.h"
24 #ifdef __BEOS__
25 #include <be/kernel/fs_info.h>
26 #include <be/kernel/OS.h>
27 #endif
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
67 /***********************************************************************
68 * usleep
70 #ifndef HAVE_USLEEP
71 int usleep (unsigned int useconds)
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;
81 delay.tv_sec = useconds / 1000000;
82 delay.tv_usec = useconds % 1000000;
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) */
91 #endif /* HAVE_USLEEP */
93 /***********************************************************************
94 * memmove
96 #ifndef HAVE_MEMMOVE
97 void *memmove( void *dest, const void *src, size_t len )
99 register char *dst = dest;
101 /* Use memcpy if not overlapping */
102 if ((dst + len <= (char *)src) || ((char *)src + len <= dst))
104 memcpy( dst, src, len );
106 /* Otherwise do it the hard way (FIXME: could do better than this) */
107 else if (dst < (char *)src)
109 while (len--) *dst++ = *((char *)src)++;
111 else
113 dst += len - 1;
114 src = (char *)src + len - 1;
115 while (len--) *dst-- = *((char *)src)--;
117 return dest;
119 #endif /* HAVE_MEMMOVE */
121 /***********************************************************************
122 * strerror
124 #ifndef HAVE_STRERROR
125 const char *strerror( int err )
127 /* Let's hope we have sys_errlist then */
128 return sys_errlist[err];
130 #endif /* HAVE_STRERROR */
133 /***********************************************************************
134 * getpagesize
136 #ifndef HAVE_GETPAGESIZE
137 size_t getpagesize(void)
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
147 #endif /* HAVE_GETPAGESIZE */
150 /***********************************************************************
151 * clone
153 #if !defined(HAVE_CLONE) && defined(__linux__)
154 int clone( int (*fn)(void *), void *stack, int flags, void *arg )
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__ */
182 #endif /* !HAVE_CLONE && __linux__ */
184 /***********************************************************************
185 * strcasecmp
187 #ifndef HAVE_STRCASECMP
188 int strcasecmp( const char *str1, const char *str2 )
190 const unsigned char *ustr1 = (const unsigned char *)str1;
191 const unsigned char *ustr2 = (const unsigned char *)str2;
193 while (*ustr1 && toupper(*ustr1) == toupper(*ustr2)) {
194 ustr1++;
195 ustr2++;
197 return toupper(*ustr1) - toupper(*ustr2);
199 #endif /* HAVE_STRCASECMP */
201 /***********************************************************************
202 * strncasecmp
204 #ifndef HAVE_STRNCASECMP
205 int strncasecmp( const char *str1, const char *str2, size_t n )
207 const unsigned char *ustr1 = (const unsigned char *)str1;
208 const unsigned char *ustr2 = (const unsigned char *)str2;
209 int res;
211 if (!n) return 0;
212 while ((--n > 0) && *ustr1) {
213 if ((res = toupper(*ustr1) - toupper(*ustr2))) return res;
214 ustr1++;
215 ustr2++;
217 return toupper(*ustr1) - toupper(*ustr2);
219 #endif /* HAVE_STRNCASECMP */
221 /***********************************************************************
222 * getsockopt
224 #ifndef HAVE_GETSOCKOPT
225 int getsockopt(int socket, int level, int option_name,
226 void *option_value, size_t *option_len)
228 errno = ENOSYS;
229 return -1;
231 #endif /* !defined(HAVE_GETSOCKOPT) */
233 /***********************************************************************
234 * inet_network
236 #ifndef HAVE_INET_NETWORK
237 unsigned long inet_network(const char *cp)
239 errno = ENOSYS;
240 return 0;
242 #endif /* defined(HAVE_INET_NETWORK) */
244 /***********************************************************************
245 * statfs
247 #ifndef HAVE_STATFS
248 int statfs(const char *name, struct statfs *info)
250 #ifdef __BEOS__
251 dev_t mydev;
252 fs_info fsinfo;
254 if(!info) {
255 errno = ENOSYS;
256 return -1;
259 if ((mydev = dev_for_path(name)) < 0) {
260 errno = ENOSYS;
261 return -1;
264 if (fs_stat_dev(mydev,&fsinfo) < 0) {
265 errno = ENOSYS;
266 return -1;
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__) */
278 #endif /* !defined(HAVE_STATFS) */
281 /***********************************************************************
282 * lstat
284 #ifndef HAVE_LSTAT
285 int lstat(const char *file_name, struct stat *buf)
287 return stat( file_name, buf );
289 #endif /* HAVE_LSTAT */
291 /***********************************************************************
292 * mkstemp
294 #ifndef HAVE_MKSTEMP
295 int mkstemp(char *tmpfn)
297 int tries;
298 char *xstart;
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';
316 return -1;
318 #endif /* HAVE_MKSTEMP */
321 /***********************************************************************
322 * pread
324 * FIXME: this is not thread-safe
326 #ifndef HAVE_PREAD
327 ssize_t pread( int fd, void *buf, size_t count, off_t offset )
329 ssize_t ret;
330 off_t old_pos;
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)
336 int err = errno; /* save errno */
337 lseek( fd, old_pos, SEEK_SET );
338 errno = err;
339 return -1;
341 if (lseek( fd, old_pos, SEEK_SET ) == -1) return -1;
342 return ret;
344 #endif /* HAVE_PREAD */
347 /***********************************************************************
348 * pwrite
350 * FIXME: this is not thread-safe
352 #ifndef HAVE_PWRITE
353 ssize_t pwrite( int fd, const void *buf, size_t count, off_t offset )
355 ssize_t ret;
356 off_t old_pos;
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)
362 int err = errno; /* save errno */
363 lseek( fd, old_pos, SEEK_SET );
364 errno = err;
365 return -1;
367 if (lseek( fd, old_pos, SEEK_SET ) == -1) return -1;
368 return ret;
370 #endif /* HAVE_PWRITE */
373 #if defined(__svr4__) || defined(__NetBSD__)
374 /***********************************************************************
375 * try_mmap_fixed
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.
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.
391 static int try_mmap_fixed (void *addr, size_t len, int prot, int flags,
392 int fildes, off_t off)
394 char * volatile result = NULL;
395 int pagesize = getpagesize();
396 pid_t pid;
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. */
402 if ( !addr )
403 return 0;
404 if ( (uintptr_t)addr & (pagesize-1) )
405 return 0;
406 if ( flags & MAP_FIXED )
407 return 0;
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. */
416 if ( (pid = vfork()) == -1 )
418 perror("try_mmap_fixed: vfork");
419 exit(1);
421 if ( pid == 0 )
423 int i;
424 char vec;
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);
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);
439 if ( result != (void *) -1 ) /* This should never happen ... */
440 munmap( result, len );
442 _exit(1);
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. */
449 return result == addr;
451 #endif
453 /***********************************************************************
454 * wine_anon_mmap
456 * Portable wrapper for anonymous mmaps
458 void *wine_anon_mmap( void *start, size_t size, int prot, int flags )
460 #ifdef HAVE_MMAP
461 static int fdzero = -1;
463 #ifdef MAP_ANON
464 flags |= MAP_ANON;
465 #else
466 if (fdzero == -1)
468 if ((fdzero = open( "/dev/zero", O_RDONLY )) == -1)
470 perror( "/dev/zero: open" );
471 exit(1);
474 #endif /* MAP_ANON */
476 #ifdef MAP_SHARED
477 flags &= ~MAP_SHARED;
478 #endif
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
485 #if defined(__svr4__) || defined(__NetBSD__)
486 if ( try_mmap_fixed( start, size, prot, flags, fdzero, 0 ) )
487 return start;
488 #endif
490 return mmap( start, size, prot, flags, fdzero, 0 );
491 #else
492 return (void *)-1;
493 #endif
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.
507 /***********************************************************************
508 * wine_dlopen
510 void *wine_dlopen( const char *filename, int flag, char *error, int errorsize )
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)
520 strncpy( error, s ? s : "", errorsize );
521 error[errorsize - 1] = '\0';
523 dlerror();
524 return ret;
525 #else
526 if (error)
528 strncpy( error, "dlopen interface not detected by configure", errorsize );
529 error[errorsize - 1] = '\0';
531 return NULL;
532 #endif
535 /***********************************************************************
536 * wine_dlsym
538 void *wine_dlsym( void *handle, const char *symbol, char *error, int errorsize )
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)
548 strncpy( error, s ? s : "", errorsize );
549 error[errorsize - 1] = '\0';
551 dlerror();
552 return ret;
553 #else
554 if (error)
556 strncpy( error, "dlopen interface not detected by configure", errorsize );
557 error[errorsize - 1] = '\0';
559 return NULL;
560 #endif
563 /***********************************************************************
564 * wine_dlclose
566 int wine_dlclose( void *handle, char *error, int errorsize )
568 #ifdef HAVE_DLOPEN
569 int ret;
570 const char *s;
571 dlerror(); dlerror();
572 ret = dlclose( handle );
573 s = dlerror();
574 if (error)
576 strncpy( error, s ? s : "", errorsize );
577 error[errorsize - 1] = '\0';
579 dlerror();
580 return ret;
581 #else
582 if (error)
584 strncpy( error, "dlopen interface not detected by configure", errorsize );
585 error[errorsize - 1] = '\0';
587 return 1;
588 #endif
591 /***********************************************************************
592 * wine_rewrite_s4tos2
594 * Convert 4 byte Unicode strings to 2 byte Unicode strings in-place.
595 * This is only practical if literal strings are writable.
597 unsigned short* wine_rewrite_s4tos2(const wchar_t* str4 )
599 unsigned short *str2,*s2;
601 if (str4==NULL)
602 return NULL;
604 if ((*str4 & 0xffff0000) != 0) {
605 /* This string has already been converted. Return it as is */
606 return (unsigned short*)str4;
609 /* Note that we can also end up here if the string has a single
610 * character. In such a case we will convert the string over and
611 * over again. But this is harmless.
613 str2=s2=(unsigned short*)str4;
614 do {
615 *s2=(unsigned short)*str4;
616 s2++;
617 } while (*str4++ != L'\0');
619 return str2;
622 #ifndef HAVE_ECVT
623 /***********************************************************************
624 * ecvt
626 char *ecvt (double number, int ndigits, int *decpt, int *sign)
628 static char buf[40]; /* ought to be enough */
629 char *dec;
630 sprintf(buf, "%.*e", ndigits /* FIXME wrong */, number);
631 *sign = (number < 0);
632 dec = strchr(buf, '.');
633 *decpt = (dec) ? (int)dec - (int)buf : -1;
634 return buf;
636 #endif /* HAVE_ECVT */
638 #ifndef HAVE_FCVT
639 /***********************************************************************
640 * fcvt
642 char *fcvt (double number, int ndigits, int *decpt, int *sign)
644 static char buf[40]; /* ought to be enough */
645 char *dec;
646 sprintf(buf, "%.*e", ndigits, number);
647 *sign = (number < 0);
648 dec = strchr(buf, '.');
649 *decpt = (dec) ? (int)dec - (int)buf : -1;
650 return buf;
652 #endif /* HAVE_FCVT */
654 #ifndef HAVE_GCVT
655 /***********************************************************************
656 * gcvt
658 * FIXME: uses both E and F.
660 char *gcvt (double number, size_t ndigit, char *buff)
662 sprintf(buff, "%.*E", (int)ndigit, number);
663 return buff;
665 #endif /* HAVE_GCVT */
668 #ifndef wine_memcpy_unaligned
669 /***********************************************************************
670 * wine_memcpy_unaligned
672 * This is necessary to defeat optimizations of memcpy by gcc.
674 void *wine_memcpy_unaligned( void *dst, const void *src, size_t size )
676 return memcpy( dst, src, size );
678 #endif
681 /***********************************************************************
682 * pthread functions
685 #ifndef HAVE_PTHREAD_GETSPECIFIC
686 void pthread_getspecific() { assert(0); }
687 #endif
689 #ifndef HAVE_PTHREAD_KEY_CREATE
690 void pthread_key_create() { assert(0); }
691 #endif
693 #ifndef HAVE_PTHREAD_MUTEX_LOCK
694 void pthread_mutex_lock() { assert(0); }
695 #endif
697 #ifndef HAVE_PTHREAD_MUTEX_UNLOCK
698 void pthread_mutex_unlock() { assert(0); }
699 #endif
701 #ifndef HAVE_PTHREAD_SETSPECIFIC
702 void pthread_setspecific() { assert(0); }
703 #endif
705 /***********************************************************************
706 * interlocked functions
708 #ifdef __i386__
710 #ifdef __GNUC__
712 __ASM_GLOBAL_FUNC(interlocked_cmpxchg,
713 "movl 12(%esp),%eax\n\t"
714 "movl 8(%esp),%ecx\n\t"
715 "movl 4(%esp),%edx\n\t"
716 "lock; cmpxchgl %ecx,(%edx)\n\t"
717 "ret");
718 __ASM_GLOBAL_FUNC(interlocked_cmpxchg_ptr,
719 "movl 12(%esp),%eax\n\t"
720 "movl 8(%esp),%ecx\n\t"
721 "movl 4(%esp),%edx\n\t"
722 "lock; cmpxchgl %ecx,(%edx)\n\t"
723 "ret");
724 __ASM_GLOBAL_FUNC(interlocked_xchg,
725 "movl 8(%esp),%eax\n\t"
726 "movl 4(%esp),%edx\n\t"
727 "lock; xchgl %eax,(%edx)\n\t"
728 "ret");
729 __ASM_GLOBAL_FUNC(interlocked_xchg_ptr,
730 "movl 8(%esp),%eax\n\t"
731 "movl 4(%esp),%edx\n\t"
732 "lock; xchgl %eax,(%edx)\n\t"
733 "ret");
734 __ASM_GLOBAL_FUNC(interlocked_xchg_add,
735 "movl 8(%esp),%eax\n\t"
736 "movl 4(%esp),%edx\n\t"
737 "lock; xaddl %eax,(%edx)\n\t"
738 "ret");
740 #elif defined(_MSC_VER)
742 __declspec(naked) long interlocked_cmpxchg( long *dest, long xchg, long compare )
744 __asm mov eax, 12[esp];
745 __asm mov ecx, 8[esp];
746 __asm mov edx, 4[esp];
747 __asm lock cmpxchg [edx], ecx;
748 __asm ret;
751 __declspec(naked) void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare )
753 __asm mov eax, 12[esp];
754 __asm mov ecx, 8[esp];
755 __asm mov edx, 4[esp];
756 __asm lock cmpxchg [edx], ecx;
757 __asm ret;
760 __declspec(naked) long interlocked_xchg( long *dest, long val )
762 __asm mov eax, 8[esp];
763 __asm mov edx, 4[esp];
764 __asm lock xchg [edx], eax;
765 __asm ret;
768 __declspec(naked) void *interlocked_xchg_ptr( void **dest, void *val )
770 __asm mov eax, 8[esp];
771 __asm mov edx, 4[esp];
772 __asm lock xchg [edx], eax;
773 __asm ret;
776 __declspec(naked) long interlocked_xchg_add( long *dest, long incr )
778 __asm mov eax, 8[esp];
779 __asm mov edx, 4[esp];
780 __asm lock xadd [edx], eax;
781 __asm ret;
784 #else
785 # error You must implement the interlocked* functions for your compiler
786 #endif
788 #elif defined(__powerpc__)
789 void* interlocked_cmpxchg_ptr( void **dest, void* xchg, void* compare)
791 long ret = 0;
792 long scratch;
793 __asm__ __volatile__(
794 "0: lwarx %0,0,%2 ;"
795 " xor. %1,%4,%0;"
796 " bne 1f;"
797 " stwcx. %3,0,%2;"
798 " bne- 0b;"
799 "1: "
800 : "=&r"(ret), "=&r"(scratch)
801 : "r"(dest), "r"(xchg), "r"(compare)
802 : "cr0","memory");
803 return (void*)ret;
806 long interlocked_cmpxchg( long *dest, long xchg, long compare)
808 long ret = 0;
809 long scratch;
810 __asm__ __volatile__(
811 "0: lwarx %0,0,%2 ;"
812 " xor. %1,%4,%0;"
813 " bne 1f;"
814 " stwcx. %3,0,%2;"
815 " bne- 0b;"
816 "1: "
817 : "=&r"(ret), "=&r"(scratch)
818 : "r"(dest), "r"(xchg), "r"(compare)
819 : "cr0","memory");
820 return ret;
823 long interlocked_xchg_add( long *dest, long incr )
825 long ret = 0;
826 long zero = 0;
827 __asm__ __volatile__(
828 "0: lwarx %0, %3, %1;"
829 " add %0, %2, %0;"
830 " stwcx. %0, %3, %1;"
831 " bne- 0b;"
832 : "=&r" (ret)
833 : "r"(dest), "r"(incr), "r"(zero)
834 : "cr0", "memory"
836 return ret-incr;
839 long interlocked_xchg( long* dest, long val )
841 long ret = 0;
842 __asm__ __volatile__(
843 "0: lwarx %0,0,%1 ;"
844 " stwcx. %2,0,%1;"
845 " bne- 0b;"
846 : "=&r"(ret)
847 : "r"(dest), "r"(val)
848 : "cr0","memory");
849 return ret;
852 void* interlocked_xchg_ptr( void** dest, void* val )
854 void *ret = NULL;
855 __asm__ __volatile__(
856 "0: lwarx %0,0,%1 ;"
857 " stwcx. %2,0,%1;"
858 " bne- 0b;"
859 : "=&r"(ret)
860 : "r"(dest), "r"(val)
861 : "cr0","memory");
862 return ret;
865 #elif defined(__sparc__) && defined(__sun__)
868 * As the earlier Sparc processors lack necessary atomic instructions,
869 * I'm simply falling back to the library-provided _lwp_mutex routines
870 * to ensure mutual exclusion in a way appropriate for the current
871 * architecture.
873 * FIXME: If we have the compare-and-swap instruction (Sparc v9 and above)
874 * we could use this to speed up the Interlocked operations ...
876 #include <synch.h>
877 static lwp_mutex_t interlocked_mutex = DEFAULTMUTEX;
879 long interlocked_cmpxchg( long *dest, long xchg, long compare )
881 _lwp_mutex_lock( &interlocked_mutex );
882 if (*dest == compare) *dest = xchg;
883 else compare = *dest;
884 _lwp_mutex_unlock( &interlocked_mutex );
885 return compare;
888 void *interlocked_cmpxchg_ptr( void **dest, void *xchg, void *compare )
890 _lwp_mutex_lock( &interlocked_mutex );
891 if (*dest == compare) *dest = xchg;
892 else compare = *dest;
893 _lwp_mutex_unlock( &interlocked_mutex );
894 return compare;
897 long interlocked_xchg( long *dest, long val )
899 long retv;
900 _lwp_mutex_lock( &interlocked_mutex );
901 retv = *dest;
902 *dest = val;
903 _lwp_mutex_unlock( &interlocked_mutex );
904 return retv;
907 void *interlocked_xchg_ptr( void **dest, void *val )
909 long retv;
910 _lwp_mutex_lock( &interlocked_mutex );
911 retv = *dest;
912 *dest = val;
913 _lwp_mutex_unlock( &interlocked_mutex );
914 return retv;
917 long interlocked_xchg_add( long *dest, long incr )
919 long retv;
920 _lwp_mutex_lock( &interlocked_mutex );
921 retv = *dest;
922 *dest += incr;
923 _lwp_mutex_unlock( &interlocked_mutex );
924 return retv;
926 #else
927 # error You must implement the interlocked* functions for your CPU
928 #endif