/* $Id: mutex.c,v 1.2 2002/02/20 09:17:57 hyperion Exp $
 */
/*
 * COPYRIGHT:   See COPYING in the top level directory
 * PROJECT:     ReactOS POSIX+ Subsystem
 * FILE:        subsys/psx/lib/psxdll/pthread/mutex.c
 * PURPOSE:     Mutex functions
 * PROGRAMMER:  KJK::Hyperion <noog@libero.it>
 * UPDATE HISTORY:
 *              19/12/2001: Created
 */

#include <ntos.h>
#include <ddk/ntddk.h>
#include <sys/types.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <psx/debug.h>
#include <psx/pthread.h>
#include <psx/errno.h>
#include <psx/safeobj.h>

int pthread_mutex_init(pthread_mutex_t *mutex,
    const pthread_mutexattr_t *attr)
{
 struct __mutex     *pmMutex;
 struct __mutexattr *pmaMutexAttrs;
 BOOL                 bShared;
 OBJECT_ATTRIBUTES    oaMutexAttrs;
 NTSTATUS             nErrCode;

 /* invalid return buffer */
 if(mutex == NULL)
  return (EINVAL);

 /* object still open */
 if(__safeobj_validate(*mutex, __PTHREAD_MUTEX_MAGIC))
  return (EBUSY);

 if(attr == NULL)
 {
  /* use default attributes */
  /* create new mutex object */
  pmMutex = (struct __mutex *)malloc(sizeof(struct __mutex));

  /* malloc() failure */
  if(!pmMutex)
   return (ENOMEM);

  /* set the attributes */
  bShared = FALSE;
  pmMutex->type = PTHREAD_MUTEX_RECURSIVE;
 }
 else if(__safeobj_validate(*attr, __PTHREAD_MUTEX_ATTR_MAGIC))
 {
  /* use provided attributes */
  /* create new mutex object */
  pmMutex = (struct __mutex *)malloc(sizeof(struct __mutex));

  /* malloc() failure */
  if(!pmMutex)
   return (ENOMEM);

  /* get the attributes object */
  pmaMutexAttrs = (struct __mutexattr *) *attr;

  /* set the attributes */
  bShared = (pmaMutexAttrs->pshared != PTHREAD_PROCESS_PRIVATE);
  pmMutex->type = pmaMutexAttrs->type;
 }
 else
  return (EINVAL);

 /* necessary for the mutex to be considered valid later */
 pmMutex->signature = __PTHREAD_MUTEX_MAGIC;

 /* creation of the native mutex object */
 pmMutex->handle = 0;

 /* initialize generic object attributes */
 oaMutexAttrs.Length = sizeof(OBJECT_ATTRIBUTES);
 oaMutexAttrs.RootDirectory = NULL;
 oaMutexAttrs.ObjectName = NULL;
 oaMutexAttrs.Attributes = 0;
 oaMutexAttrs.SecurityDescriptor = NULL;
 oaMutexAttrs.SecurityQualityOfService = NULL;

 /* process-exclusive mutex */
 if(bShared)
  oaMutexAttrs.Attributes |= OBJ_EXCLUSIVE;

 /* try to create the object */
 nErrCode = NtCreateMutant
 (
  &pmMutex->handle,
  MUTANT_ALL_ACCESS,
  &oaMutexAttrs,
  FALSE
 );

 /* failure */
 if(!NT_SUCCESS(nErrCode))
 {
  /* free the internal mutex object */
  free(pmMutex);
  /* return errno */
  return (__status_to_errno(nErrCode));
 }

 /* return the pointer to the mutex */
 *mutex = (pthread_mutex_t)pmMutex;

 /* success */
 return (0);

}

int pthread_mutex_destroy(pthread_mutex_t *mutex)
{
 struct __mutex         *pmMutex;
 NTSTATUS                 nErrCode;
 MUTANT_BASIC_INFORMATION mbiMutexInfo;

 /* invalid pointer or pointer to invalid object */
 if(mutex == NULL || !__safeobj_validate(*mutex, __PTHREAD_MUTEX_MAGIC))
 {
  return (EINVAL);
 }

 pmMutex = (struct __mutex *)*mutex;

 /* query the mutex's status */
 nErrCode = NtQueryMutant
 (
  pmMutex->handle,
  MutantBasicInformation,
  &mbiMutexInfo,
  sizeof(MUTANT_BASIC_INFORMATION),
  NULL
 );

 /* failure */
 if(!NT_SUCCESS(nErrCode))
 {
  return (__status_to_errno(nErrCode));
 }

 /* the thread is owned - cannot destroy it */
 if(mbiMutexInfo.Count <= 0)
 {
  return (EBUSY);
 }

 /* try to close the handle */
 nErrCode = NtClose(pmMutex->handle);

 /* failure */
 if(!NT_SUCCESS(nErrCode))
 {
  return (__status_to_errno(nErrCode));
 }

 /* free the object, nil the pointer */
 free(*mutex);
 *mutex = NULL;

 /* success */
 return (0);

}

int pthread_mutex_lock(pthread_mutex_t *mutex)
{
 struct __mutex * pmMutex;
 NTSTATUS nErrCode;

 /* invalid pointer or pointer to invalid object */
 if(mutex == NULL || !__safeobj_validate(*mutex, __PTHREAD_MUTEX_MAGIC))
  return (EINVAL);

 pmMutex = (struct __mutex *)*mutex;

 /* decide the behavior from the mutex type */
 switch(pmMutex->type)
 {
  case PTHREAD_MUTEX_NORMAL:
  {
   /* unconditionally try to lock the mutex */
   /* FIXME? should we "artificially" hang the thread if it's the mutex owner, since
      NT mutexes always behave recursively? */

#if 0
   if(0 /* mutex owner */ == pthread_self() */)
    NtDelayExecution(FALSE, NULL);
#endif

   nErrCode = NtWaitForSingleObject(pmMutex->handle, FALSE, NULL);
   break;
  }

  case PTHREAD_MUTEX_ERRORCHECK:
  {
   /* prevent a thread from recursively locking the same mutex */
   if(0 /* mutex owner */ == pthread_self()) /* FIXME: implement the correct logic */
    return (EDEADLK);
   else
    nErrCode = NtWaitForSingleObject(pmMutex->handle, FALSE, NULL);

   break;
  }

  case PTHREAD_MUTEX_RECURSIVE:
  {
   /* allow recursive locking */
   /* ASSERT: this is the default behavior for NT */
   nErrCode = NtWaitForSingleObject(pmMutex->handle, FALSE, NULL);
   break;
  }

  default:
   /* we should never reach this point */
   INFO("you should never read this");

 }

 if(nErrCode == STATUS_ABANDONED)
 {
  FIXME("mutex abandoned, not sure on what to do: should we try to lock the mutex again?");
 }
 else if(!NT_SUCCESS(nErrCode))
 {
  return (__status_to_errno(nErrCode));
 }

 /* success */
 return (0);

}

int pthread_mutex_trylock(pthread_mutex_t *mutex)
{
 struct __mutex * pmMutex;
 NTSTATUS nErrCode;
 MUTANT_BASIC_INFORMATION mbiMutexInfo;

 /* invalid pointer or pointer to invalid object */
 if(mutex == NULL || !__safeobj_validate(*mutex, __PTHREAD_MUTEX_MAGIC))
  return (EINVAL);

 pmMutex = (struct __mutex *)*mutex;

 /* query the mutex's status */
 nErrCode = NtQueryMutant
 (
  pmMutex->handle,
  MutantBasicInformation,
  &mbiMutexInfo,
  sizeof(MUTANT_BASIC_INFORMATION),
  NULL
 );

 /* failure */
 if(!NT_SUCCESS(nErrCode))
 {
  return (__status_to_errno(nErrCode));
 }

 /* mutex already locked */
 if(mbiMutexInfo.Count <= 0)
  return (EBUSY);

 /* mutex not locked - mutex type attribute doesn't matter */
 nErrCode = NtWaitForSingleObject(pmMutex->handle, FALSE, NULL);

 if(!NT_SUCCESS(nErrCode))
 {
  return (__status_to_errno(nErrCode));
 }

 /* success */
 return (0);

}

int pthread_mutex_unlock(pthread_mutex_t *mutex)
{
 struct __mutex * pmMutex;
 NTSTATUS nErrCode;

 /* invalid pointer or pointer to invalid object */
 if(mutex == NULL || !__safeobj_validate(*mutex, __PTHREAD_MUTEX_MAGIC))
  return (EINVAL);

 pmMutex = (struct __mutex *)*mutex;

 /* try to release the mutex */
 nErrCode = NtReleaseMutant(pmMutex->handle, NULL);

 /* failure */
 if(!NT_SUCCESS(nErrCode))
 {
  return (__status_to_errno(nErrCode));
 }

 /* success */
 return (0);

}

/* mutex attributes routines */

int pthread_mutexattr_init(pthread_mutexattr_t *attr)
{
 struct __mutexattr * pmaMutexAttrs;

 /* invalid return pointer */
 if(!attr)
  return (EINVAL);

 /* allocate internal structure for mutex attributes */
 pmaMutexAttrs = (struct __mutexattr *)malloc(sizeof(struct __mutexattr));

 /* failure */
 if(pmaMutexAttrs == 0)
  return (ENOMEM);

 /* attribute defaults */
 pmaMutexAttrs->pshared = PTHREAD_PROCESS_PRIVATE;
 pmaMutexAttrs->type = PTHREAD_MUTEX_DEFAULT;

 /* return the pointer to the attributes object */
 *attr = (pthread_mutexattr_t)pmaMutexAttrs;

 /* success */
 return (0);

}

int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
{
 /* invalid pointer or pointer to invalid object */
 if(attr == NULL || !__safeobj_validate(*attr, __PTHREAD_MUTEX_ATTR_MAGIC))
  return (EINVAL);

 /* deallocate internal structure */
 free(*attr);

 /* success */
 return (0);

}

#define PTHREAD_MUTEXATTR_GET(PATTR,PVAR,FIELD) \
 if( \
  (PATTR) == NULL || \
  (PVAR) == NULL || \
  !__safeobj_validate(*(PATTR), __PTHREAD_MUTEX_ATTR_MAGIC) \
 ) \
  return (EINVAL); \
 else \
 { \
  (*(PVAR)) = ((struct __mutexattr *)*(PATTR))->FIELD; \
  return (0); \
 }

int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr,
    int *pshared)
{
 PTHREAD_MUTEXATTR_GET(attr, pshared, pshared)
}

int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type)
{
 PTHREAD_MUTEXATTR_GET(attr, type, type)
}

int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,
    int pshared)
{
 /* invalid pointer or pointer to invalid object */
 if(attr == NULL || !__safeobj_validate(*attr, __PTHREAD_MUTEX_ATTR_MAGIC))
  return (EINVAL);

 /* validate value */
 switch(pshared)
 {
  case PTHREAD_PROCESS_SHARED: break;
  case PTHREAD_PROCESS_PRIVATE: break;
  default: return (EINVAL);
 }

 ((struct __mutexattr *)*attr)->pshared = pshared;

 return (0);

}

int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
{
 /* invalid pointer or pointer to invalid object */
 if(attr == NULL || !__safeobj_validate(*attr, __PTHREAD_MUTEX_ATTR_MAGIC))
  return (EINVAL);

 /* validate value */
 switch(type)
 {
  case PTHREAD_MUTEX_NORMAL: break;
  case PTHREAD_MUTEX_ERRORCHECK: break;
  case PTHREAD_MUTEX_RECURSIVE: break;
  default: return (EINVAL);
 }

 ((struct __mutexattr *)*attr)->type = type;

 return (0);

}

/* STUBS */

int pthread_mutex_setprioceiling(pthread_mutex_t *mutex,
    int prioceiling, int *old_ceiling)
{
 TODO("realtime threads not currently implemented");
 return (ENOSYS);
}

int pthread_mutex_getprioceiling(const pthread_mutex_t *mutex,
    int *prioceiling)
{
 TODO("realtime threads not currently implemented");
 return (ENOSYS);
}

int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr,
    int *protocol)
{
 TODO("realtime threads not currently implemented");
 return (ENOSYS);
}

int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr,
    int protocol)
{
 TODO("realtime threads not currently implemented");
 return (ENOSYS);
}

int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr,
    int prioceiling)
{
 TODO("realtime threads not currently implemented");
 return (ENOSYS);
}

int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *attr,
    int *prioceiling)
{
 TODO("realtime threads not currently implemented");
 return (ENOSYS);
}

/* EOF */

