egl/eglrefimpl/src/syncobj.cpp
author Jose Thachil<jose.thachil@cell-telecom.com>
Wed, 19 May 2010 14:34:22 +0100
branchEGL_MERGE
changeset 75 b3f964e007c8
parent 0 5d03bc08d59c
permissions -rw-r--r--
Better way to delete CEglThreadSession.

// Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// Reference EGL implementation to support EGL sync objects and OpenWF extensions

#include "eglprivate.h"

CEglSync::CEglSync(CEglDisplay& aDisplay):
    iDisplay(aDisplay),
    iType(EGL_SYNC_REUSABLE_KHR),
    iStatus(EGL_UNSIGNALED_KHR)
    {
    }

CEglSync::~CEglSync()
    {
    iCondVar.Close();
    iMutex.Close();
    }

TInt CEglSync::Construct()
    {
    TInt err = iMutex.CreateLocal();
    if (err != KErrNone)
        {
        return err;
        }

    err = iCondVar.CreateLocal();
    if (err != KErrNone)
        {
        return err;
        }
    
    err = iDisplay.RegisterSyncObj(*this);
    if (err != KErrNone)
        {
        return err;
        }

    iRefCount = 1;
    return KErrNone;
    }

CEglSync* CEglSync::Create(CEglDisplay& aDisplay)
    {
    // sync object will be allocated in the EGL shared heap and added to sync object list
    // we need to switch current heap to EGL shared heap
    //
    RHeap* callerHeap = User::SwitchHeap(&aDisplay.Heap());

    CEglSync* syncObj = new CEglSync(aDisplay);
    if (!syncObj)
        {
        User::SwitchHeap(callerHeap);
        return NULL;
        }
    
    const TInt err = syncObj->Construct();
    if (err != KErrNone)
        {
        delete syncObj;
        User::SwitchHeap(callerHeap);
        return NULL;
        }
    
    User::SwitchHeap(callerHeap);
    return syncObj;
    }

void CEglSync::Destroy()
    {
    // multiple calls to Destroy() is not allowed, it's either coming from eglDestroySyncKHR or eglTerminate
    //
    __ASSERT_DEBUG(!iIsDestroyed, User::Panic(KEglPanicCategory, EEglPanicSyncObjHasBeenDestroyed));
    
    iIsDestroyed = ETrue;

    // wake up all waiting threads
    iCondVar.Broadcast();

    // always remove sync obj from hash map when it is destroyed, the actual deletion will be done from Close(),
    // which can happen when eglClientWaitSyncKHR is called by user
    RHeap* callerHeap = User::SwitchHeap(&iDisplay.Heap());
    iDisplay.UnregisterSyncObj(*this);
    User::SwitchHeap(callerHeap);
    
    // decrement refcount for this sync obj, it will delete the object if refcount is zero
    Close();
    }

void CEglSync::Close()
    {
    if (--iRefCount == 0)
        {
        // we're here either from Destroy() or eglClientWaitSyncKHR
        RHeap* callerHeap = User::SwitchHeap(&iDisplay.Heap());
        delete this;
        User::SwitchHeap(callerHeap);
        }
    }

void CEglSync::Signal(EGLenum aMode)
    {
    iMutex.Wait();
    if (iStatus != aMode)
        {
        iStatus = aMode;
        if (iStatus == EGL_SIGNALED_KHR)
            {
            iCondVar.Broadcast();
            }
        }
    iMutex.Signal();
    }

EGLint CEglSync::Wait(EGLTimeKHR aTimeOut)
    {
    // driver display lock is not held when we're about to enter block wait on condition variable
    // we use sync object mutex to synchronise threads access from this point until end of this function
    iMutex.Wait();
    EGLint errCode = EGL_CONDITION_SATISFIED_KHR;
    
    if (iStatus == EGL_UNSIGNALED_KHR)
        {
        switch(aTimeOut) 
            {
            case EGL_FOREVER_KHR:
                {
                const TInt res = iCondVar.Wait(iMutex); 
                //we do not expect to fail here
                __ASSERT_DEBUG(res == KErrNone, User::Panic(KEglPanicCategory, EEglPanicCondVarWaitFail));
                break;
                }
            case 0:
                {
                //by setting this we notify the caller that the sync object is in unsignaled state
                errCode = EGL_TIMEOUT_EXPIRED_KHR; 
                break;
                }
            default:
                {
                // Since the supported range of timeout at function RCondVar::TimedWait(mutex, timeout) 
                // is 0 to KMaxTInt, looping mechanism below is used to support 64bit timeout.      
                //
                TInt res = KErrTimedOut;
                for(TInt64 timeoutMicroseconds = aTimeOut/1000; (res == KErrTimedOut) && (timeoutMicroseconds > 0); timeoutMicroseconds -= KMaxTInt)
                    {
                    res = iCondVar.TimedWait(iMutex, (timeoutMicroseconds > KMaxTInt?KMaxTInt:timeoutMicroseconds));
                    //we do not expect to fail here
                    __ASSERT_DEBUG(res == KErrNone || res == KErrTimedOut, User::Panic(KEglPanicCategory, EEglPanicCondVarTimedWaitFail));
                    }
                if(res == KErrTimedOut)
                    {
                    errCode = EGL_TIMEOUT_EXPIRED_KHR;
                    }
                break;
                }
            }
        }

    iMutex.Signal();
    return errCode;
    }