#include "third_party/chibicc/test/test.h"

#define SPINLOCK(lock)                                \
  do {                                                \
    for (;;) {                                        \
      typeof(*(lock)) x;                              \
      __atomic_load(lock, &x, __ATOMIC_RELAXED);      \
      if (!x && !__sync_lock_test_and_set(lock, 1)) { \
        break;                                        \
      } else {                                        \
        __builtin_ia32_pause();                       \
      }                                               \
    }                                                 \
  } while (0)

#define SPUNLOCK(lock) __sync_lock_release(lock)

////////////////////////////////////////////////////////////////////////////////

#define SPINLOCK2(lock)                                           \
  do {                                                            \
    for (;;) {                                                    \
      typeof(*(lock)) x;                                          \
      __atomic_load(lock, &x, __ATOMIC_RELAXED);                  \
      if (!x && !__atomic_test_and_set(lock, __ATOMIC_SEQ_CST)) { \
        break;                                                    \
      } else {                                                    \
        __builtin_ia32_pause();                                   \
      }                                                           \
    }                                                             \
  } while (0)

#define SPUNLOCK2(lock) __sync_lock_release(lock)

////////////////////////////////////////////////////////////////////////////////

_Alignas(64) char lock;

main() {
  int x, y;

  ASSERT(0, lock);
  SPINLOCK(&lock);
  ASSERT(1, lock);
  SPUNLOCK(&lock);
  ASSERT(0, lock);

  ASSERT(0, lock);
  SPINLOCK2(&lock);
  ASSERT(1, lock);
  SPUNLOCK2(&lock);
  ASSERT(0, lock);

  x = 0;
  y = 7;
  ASSERT(0, x);
  ASSERT(7, y);
  __atomic_store(&x, &y, __ATOMIC_RELAXED);
  ASSERT(7, x);
  ASSERT(7, y);

  x = 0;
  y = 7;
  ASSERT(0, x);
  ASSERT(7, y);
  __atomic_store(&x, &y, __ATOMIC_SEQ_CST);
  ASSERT(7, x);
  ASSERT(7, y);

  x = 5;
  y = __atomic_test_and_set(&x, __ATOMIC_SEQ_CST);
  ASSERT(1, x);
  ASSERT(5, y);

  x = 5;
  __atomic_clear(&x, __ATOMIC_SEQ_CST);
  ASSERT(0, x);

  //
}