/* SCE CONFIDENTIAL
 * PLAYSTATION(R)3 Programmer Tool Runtime Library 084.006
 * Copyright (C) 2006 Sony Computer Entertainment Inc.
 * All Rights Reserved.
 */

/* QueueControl::Atomic - Atomic synchronization queue control
 */

#ifndef __CELL_DAISY_ATO_QCTL_H__
#define __CELL_DAISY_ATO_QCTL_H__

#include <cell/daisy/qctl.h>
#include <cell/sync/lfqueue.h>

/* Lv2 */
#include <sys/spu_thread.h>

#undef CELL_DAISY_DEBUG_PRINTF
#define CELL_DAISY_DEBUG_PRINTF(...)
#ifdef CELL_DAISY_DEBUG_ATO_QCTL
#include <cell/daisy/daisy_debug.h>
#endif

extern "C" {
#ifdef __PPU__
	unsigned int _cellSyncLFQueuePushOpen(CellSyncLFQueue *pQueue);
	int _cellSyncLFQueuePushClose(CellSyncLFQueue *pQueue,
								  int (*fpSendSignal)(void *, uint32_t));
	int _cellSyncLFQueueHasUnfinishedConsumer(CellSyncLFQueue *pQueue,
											  unsigned int isCancelled);
	int _cellSyncLFQueueGetPushPointer(CellSyncLFQueue *pQueue, int *pPointer, unsigned int isBlocking,
									   unsigned int useEventQueue);
	int _cellSyncLFQueueCompletePushPointer(CellSyncLFQueue *pQueue, int pointer,
											int (*fpSendSignal)(void*, uint32_t));
	int _cellSyncLFQueueGetPopPointer(CellSyncLFQueue *pQueue, int *pPointer, unsigned int isBlocking,
									  int (*fpSendSignal)(void*, uint32_t), unsigned int useEventQueue);
	int _cellSyncLFQueueCompletePopPointer(CellSyncLFQueue *pQueue, int pointer,
										   int (*fpSendSignal)(void*, uint32_t));

	int _cellSyncLFQueueAttachLv2EventQueue(sys_spu_thread_t *ids, unsigned int numSpus,
											CellSyncLFQueue *pQueue);
	int _cellSyncLFQueueDetachLv2EventQueue(sys_spu_thread_t *ids, unsigned int numSpus,
											CellSyncLFQueue *pQueue);

#else
	unsigned int _cellSyncLFQueuePushOpen(uint64_t ea);
	int _cellSyncLFQueuePushClose(uint64_t ea,
								  int (*fpSendSignal)(uint64_t, uint32_t));
	int _cellSyncLFQueueHasUnfinishedConsumer(uint64_t ea, unsigned int isCancelled,
											  int (*fpWaitSignal)(void),
											  uint32_t (*fpGetId)(void));
	int _cellSyncLFQueueGetPushPointer(uint64_t ea,
									   int *pPointer,
									   int (*fpWaitSignal)(void),
									   uint32_t (*fpGetId)(void));
	int _cellSyncLFQueueSetPushPointer(uint64_t ea,
									   int pointer,
									   int (*fpSendSignal)(uint64_t, uint32_t),
									   int (*fpWaitSignal)(void),
									   uint32_t (*fpGetId)(void));
	int _cellSyncLFQueueCompletePushPointer(uint64_t ea,
											int pointer,
											int (*fpSendSignal)(uint64_t, uint32_t),
											unsigned int useEventQueue);
	int _cellSyncLFQueueGetPopPointer(uint64_t ea,
									  int *pPointer,
									  int (*fpWaitSignal)(void),
									  uint32_t (*fpGetId)(void),
									  int (*fpSendSignal)(uint64_t, uint32_t));
	int _cellSyncLFQueueCompletePopPointer(uint64_t ea,
										   int pointer,
										   int (*fpSendSignal)(uint64_t, uint32_t),
										   unsigned int useEventQueue);
#endif
}

namespace cell {
	namespace Daisy {
		namespace QueueControl {
    
			template<SizeType tSize, QueueIO tQueueIO>
			class Atomic:public Abstract<tSize, tQueueIO> {
			private:

				/* static assertion */
				static int sAssertDummy[
					(tSize < 0x8000)
					? 1 : -1];

			protected:
#ifdef __PPU__
				CellSyncLFQueue *mLFQueue;
				sys_spu_thread_t mIds[6];
				unsigned int     mNumSpus;
				void            *mSignal;
				int            (*mfpSendSignal)(void *, uint32_t);
#else
				uint64_t         mLFQueue                           __CELL_DAISY_SPU_ALIGN16__;
				uint64_t         mEaSignal                          __CELL_DAISY_SPU_ALIGN16__;
				uint32_t       (*mfpGetId)(void)                    __CELL_DAISY_SPU_ALIGN16__;
				int            (*mfpSendSignal)(uint64_t, uint32_t) __CELL_DAISY_SPU_ALIGN16__;
				int            (*mfpWaitSignal)(void)               __CELL_DAISY_SPU_ALIGN16__;
#endif
				unsigned int     mPushStartFlag  __CELL_DAISY_SPU_ALIGN16__;
#ifdef __SPU__
				/* dummy for padding */
				uint32_t         mDummy1         __CELL_DAISY_SPU_ALIGN16__; 
#endif

			public:
      
				CELL_DAISY_INLINE
				unsigned int isOutOfOrder() { return 1; }

				static const QueueControlType sQueueControlType = QCTL_TYPE_ATOMIC;
 			    static const SizeType sQueueMaxSize = 32767;

				/* dummy for padding */
				uint32_t mDummy2   __attribute__((aligned(16)));

				/* constructor definition */
#ifdef __PPU__
				explicit Atomic(){}
				explicit Atomic(
					CellSyncLFQueue *pQueue,
					sys_spu_thread_t *ids,
					unsigned int numSpus,
					void *pSignal = 0,
					int (*fpSendSignal)(void *, uint32_t) = 0
					);

#else
				explicit Atomic(){}
				explicit Atomic(
					uint64_t lockEa,
					uint64_t eaSignal = 0,
					uint32_t (*fpGetId)(void) = 0,
					int (*fpSendSignal)(uint64_t, uint32_t) = 0,
					int (*fpWaitSignal)(void) = 0
					);
#endif

				/* virtual destructor definition */
				virtual ~Atomic();
      
				uint32_t  getCompleteNumber()
					{
						return CELL_DAISY_ERROR_NOT_IMPLEMENTED;
					}

				virtual const char *getClassName()
					{
						static char __buf[64];
						cell::Daisy::_snprintf(__buf, 64, "Atomic(%s)[mLFQueue:0x%llx,tSize:%d]",
											   tQueueIO == INPUT ? "In" : "Out",
											   mLFQueue,
											   tSize);
						return (const char *)__buf;
					}
      
				virtual void dump(const char *);
      
				CELL_DAISY_INLINE
				PointerType getPointerFromSequenceNumber(int sequenceNumber)
					{
						if (tSize == 1 || tSize == 2 || tSize == 4 || tSize == 8 ||
							tSize == 16 || tSize == 32) {
							return (sequenceNumber + 2*tSize) % (2*tSize);
						} else {
							if (__builtin_expect(sequenceNumber < 2*(int)tSize && sequenceNumber >= 0, 1)) {
								return sequenceNumber;
							} else {
								return (sequenceNumber + 2*tSize) % (2*tSize);
							}
						}
					}

				/* Consumer packet#=sequenceNumber completed */
				void endCompleteConsume(uint32_t sequenceNumber) ;
      
				/* Producer packet#=sequenceNumber completed */
				void endCompleteProduce(uint32_t sequenceNumber);
      
				/* Get next Tail pointer */
				PointerType getNextTailPointer(BlockMode stall);
#ifdef __SPU__      
				/* Set next Tail pointer */
				int setNextTailPointer(BlockMode stall, PointerType pointer);
#endif /* __SPU__ */
				/* Get next Head pointer */
				PointerType getNextHeadPointer(BlockMode stall);
      
				/* Terminate produce */
				void terminate();

				/* Check if there are any unfinished consumers
				 * @param isCancelled true if last operation was cancel
				 * @retval true: unfinished consumers exist
				 */
				bool hasUnfinishedConsumer(bool isCancelled);

				/* check if I am 1st visitor */
				bool is1stVisitor();

			};
    
			/* constructor descriptions */
#ifdef __PPU__ /* PPU */

			template<SizeType tSize, QueueIO tQueueIO>
			Atomic<tSize, tQueueIO>::Atomic(
				CellSyncLFQueue *pQueue,
				sys_spu_thread_t *ids,
				unsigned int      numSpus,
				void             *pSignal,
				int             (*fpSendSignal)(void *, uint32_t)
				):
				Abstract<tSize, tQueueIO> (),
				mLFQueue                  (pQueue),
				mNumSpus                  (numSpus),
				mSignal                   (pSignal),
				mfpSendSignal             (fpSendSignal),
				mPushStartFlag            (0)
			{
				cellDaisyAssert(CELL_DAISY_EA_ATOMIC((uintptr_t)pQueue));

				cellSyncLFQueueInitialize(pQueue, (void *)16, 0, tSize,
										  (tQueueIO == INPUT) ?
										  CELL_SYNC_QUEUE_PPU2SPU :
										  CELL_SYNC_QUEUE_SPU2PPU,
										  pSignal);
				for(unsigned i=0 ; i<numSpus ; i++) {
					mIds[i] = ids[i];
				}
				cellDaisyAssert(_cellSyncLFQueueAttachLv2EventQueue(ids, numSpus, pQueue)
								== CELL_OK);
			}

			template<SizeType tSize, QueueIO tQueueIO>
			Atomic<tSize, tQueueIO>::~Atomic()
			{
				cellDaisyAssert(_cellSyncLFQueueDetachLv2EventQueue(mIds, mNumSpus, mLFQueue)
								== CELL_OK);
			}
#else /* SPU */
			template<SizeType tSize, QueueIO tQueueIO>
			Atomic<tSize, tQueueIO>::Atomic(
				uint64_t   lockEa,
				uint64_t   eaSignal,
				uint32_t (*fpGetId)(void),
				int      (*fpSendSignal)(uint64_t, uint32_t),
				int      (*fpWaitSignal)(void)
				):
				Abstract<tSize, tQueueIO> (),
				mLFQueue                   (lockEa),
				mEaSignal                 (eaSignal),
				mfpGetId                  (fpGetId),
				mfpSendSignal             (fpSendSignal),
				mfpWaitSignal             (fpWaitSignal),
				mPushStartFlag            (0)
			{
				cellDaisyAssert(CELL_DAISY_EA_ATOMIC(lockEa));

				cellDaisyAssert(cellSyncLFQueueInitialize(lockEa, 16, 0, tSize,
														  (CellSyncQueueDirection)0,
														  eaSignal)
								== CELL_OK);
			}

			template<SizeType tSize, QueueIO tQueueIO>
			Atomic<tSize, tQueueIO>::~Atomic(){}
#endif /* __PPU __ */

			/* Member functions description(s) */
    
			template<SizeType tSize, QueueIO tQueueIO>
			void Atomic<tSize, tQueueIO>::dump(const char *messages = 0)
			{
				(void)messages;
			}
    
			template<SizeType tSize, QueueIO tQueueIO>
			CELL_DAISY_INLINE
			PointerType Atomic<tSize, tQueueIO>::getNextHeadPointer(BlockMode stall)
			{
				PointerType pointer = PTR_UNAVAILABLE;
				int ret, _pointer;
				do {
#ifdef __SPU__
					ret = _cellSyncLFQueueGetPopPointer(mLFQueue, &_pointer,
														(stall == STALL) ? mfpWaitSignal : 0,
														(stall == STALL) ? mfpGetId : 0,
														mfpSendSignal);
#else
					ret = _cellSyncLFQueueGetPopPointer(mLFQueue, &_pointer,
														(stall == STALL),
														mfpSendSignal, 1);
#endif
				} while(__builtin_expect(stall == STALL && ret == CELL_SYNC_ERROR_AGAIN, 0));
				if (__builtin_expect(ret == CELL_OK, 1)) {
					pointer = _pointer;
					CELL_DAISY_DEBUG_PRINTF("%s(getNextHeadPointer): pointer=%d\n", getClassName(), pointer%tSize);
				} else if (ret == CELL_SYNC_LFQUEUE_TERMINATED) {
					CELL_DAISY_DEBUG_PRINTF("%s(getNextHeadPointer): TERMINATE received\n", getClassName());
					pointer = PTR_TERMINATED;
				}

				this->setPointer(pointer);
				return pointer;
			}
    
			/* check if I am 1st visitor */
			template<SizeType tSize, QueueIO tQueueIO>
			CELL_DAISY_INLINE
			bool Atomic<tSize, tQueueIO>::is1stVisitor()
			{
				if (__builtin_expect(!mPushStartFlag, 0)) {
					mPushStartFlag = 1;
					return _cellSyncLFQueuePushOpen(mLFQueue);
				}
				return false;
			}

			template<SizeType tSize, QueueIO tQueueIO>
			CELL_DAISY_INLINE
			PointerType Atomic<tSize,tQueueIO>::getNextTailPointer(BlockMode stall)
			{
				(void)is1stVisitor();
				PointerType pointer = PTR_UNAVAILABLE;
				int ret, _pointer;
				do {
#ifdef __SPU__
					ret = _cellSyncLFQueueGetPushPointer(mLFQueue, &_pointer,
														 (stall == STALL) ? mfpWaitSignal : 0,
														 mfpGetId);
#else
					ret = _cellSyncLFQueueGetPushPointer(mLFQueue, &_pointer, (stall == STALL), 1);
#endif
				} while(__builtin_expect(stall == STALL && ret == CELL_SYNC_ERROR_AGAIN, 0));
				if (__builtin_expect(ret == CELL_OK, 1)) {
					pointer = _pointer;
				}

				this->setPointer(pointer);
				return pointer;
 			}

#ifdef __SPU__
			template<SizeType tSize, QueueIO tQueueIO>
			CELL_DAISY_INLINE
			int Atomic<tSize,tQueueIO>::setNextTailPointer(BlockMode stall, PointerType _pointer)
			{
				(void)is1stVisitor();
				PointerType pointer = PTR_UNAVAILABLE;
				int ret;
				do {
					ret = _cellSyncLFQueueSetPushPointer(mLFQueue, _pointer,
														 mfpSendSignal,
														 (stall == STALL) ? mfpWaitSignal : 0,
														 mfpGetId);
				} while(__builtin_expect(stall == STALL && ret == CELL_SYNC_ERROR_AGAIN, 0));
				if (__builtin_expect(ret == CELL_OK, 1)) {
					pointer = _pointer;
				}

				this->setPointer(pointer);
				return ret ? QUEUE_IS_BUSY : CELL_OK;
 			}
#endif /* __SPU__ */
    
			template<SizeType tSize, QueueIO tQueueIO>
			CELL_DAISY_INLINE
			void Atomic<tSize, tQueueIO>::endCompleteConsume(uint32_t sequenceNumber)
			{
				(void)_cellSyncLFQueueCompletePopPointer(mLFQueue, sequenceNumber, mfpSendSignal
#ifdef __SPU__
														 ,1
#endif
					);
			}
    
			template<SizeType tSize, QueueIO tQueueIO>
			CELL_DAISY_INLINE
			void Atomic<tSize, tQueueIO>::endCompleteProduce(uint32_t sequenceNumber)
			{
				(void)_cellSyncLFQueueCompletePushPointer(mLFQueue, sequenceNumber, mfpSendSignal
#ifdef __SPU__
														  ,1
#endif
					);
			}

			/* Terminate produce */
			template<SizeType tSize, QueueIO tQueueIO>
			CELL_DAISY_INLINE
			void Atomic<tSize, tQueueIO>::terminate()
			{
				if (__builtin_expect(!mPushStartFlag, 0)) {
					_cellSyncLFQueuePushOpen(mLFQueue);
				}
				CELL_DAISY_DEBUG_PRINTF("%s(terminate): terminate sent\n", getClassName());
				cellDaisyAssert(_cellSyncLFQueuePushClose(mLFQueue, mfpSendSignal)
								== CELL_OK);
			}

			/* Check if there are any unfinished consumers
			 * @param isCancelled true if last operation was cancel
			 * @retval true: unfinished consumers exist
			 */
			template<SizeType tSize, QueueIO tQueueIO>
			CELL_DAISY_INLINE
			bool Atomic<tSize, tQueueIO>::hasUnfinishedConsumer(bool isCancelled)
			{
#ifdef __SPU__
				return (_cellSyncLFQueueHasUnfinishedConsumer(mLFQueue, isCancelled, mfpWaitSignal, mfpGetId)
						== CELL_SYNC_ERROR_AGAIN) ;
#else
				return (_cellSyncLFQueueHasUnfinishedConsumer(mLFQueue, isCancelled)
						== CELL_SYNC_ERROR_AGAIN) ;
#endif
			}

		} /* namespace QueueControl */    
	} /* namespace Daisy */
} /* namespace cell */

#endif /* __CELL_DAISY_ATO_QCTL_H__ */

/*
 * Local Variables:
 * mode:C++
 * tab-width:4
 * End:
 * vim:ts=4:sw=4:
 */
