//
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information. 
//

/*++



Module Name:

    shmobject.hpp

Abstract:
    Shared memory based object



--*/

#ifndef _PAL_SHMOBJECT_HPP
#define _PAL_SHMOBJECT_HPP

#include "palobjbase.hpp"
#include "pal/shm.hpp"

extern "C"
{
#include "pal/list.h"
}

namespace CorUnix
{
    class CSimpleSharedMemoryLock : public IDataLock
    {
    public:

        void
        AcquireLock(
            CPalThread *pthr,
            IDataLock **ppDataLock
            )
        {
            SHMLock();
            *ppDataLock = static_cast<IDataLock*>(this);
        };

        virtual
        void
        ReleaseLock(
            CPalThread *pthr,
            bool fDataChanged
            )
        {
            SHMRelease();
        };
    };

    typedef struct _SHMObjData
    {
        SHMPTR shmPrevObj;
        SHMPTR shmNextObj;
        BOOL fAddedToList;
        
        SHMPTR shmObjName;
        SHMPTR shmObjImmutableData;
        SHMPTR shmObjSharedData;

        LONG lProcessRefCount;
        DWORD dwNameLength;

        PalObjectTypeId eTypeId;

        PVOID pvSynchData;
    } SHMObjData;

    class CSharedMemoryObject : public CPalObjectBase
    {
        template <class T> friend void InternalDelete(T *p);
        
    protected:

        //
        // Entry on the process's named or anonymous object list
        //

        LIST_ENTRY m_le;

        //
        // The lock that guards access to that list
        //

        CRITICAL_SECTION *m_pcsObjListLock;

        //
        // The SHMObjData for this object, protected by the
        // shared memory lock.
        //
        
        SHMPTR m_shmod;

        //
        // The shared data (i.e., m_shmObjData->shmObjSharedData)
        // for this object, mapped into this process. This will be
        // NULL if m_pot->dwSharedDataSize is 0. Access to this data
        // is controlled by m_ssmlSharedData when m_ObjectDomain is
        // SharedObject, and m_sdlSharedData when it is ProcessLocalObject.
        //

        VOID *m_pvSharedData;
        
        CSimpleSharedMemoryLock m_ssmlSharedData;
        CSimpleDataLock m_sdlSharedData;

        //
        // Is this object process local or shared?
        //
        
        ObjectDomain m_ObjectDomain;

        //
        // m_fSharedDataDereferenced will be TRUE if DereferenceSharedData
        // has already been called. (N.B. -- this is a LONG instead of a bool
        // because it is passed to InterlockedExchange). If the shared data blob
        // should be freed in the object's destructor (i.e., SHMfree should be
        // called on the appropriate SHMPTRs) DereferenceSharedData will
        // set m_fDeleteSharedData to TRUE.
        //

        LONG m_fSharedDataDereferenced;
        LONG m_fDeleteSharedData;

        PAL_ERROR
        AllocateSharedDataItems(
            SHMPTR *pshmObjData,
            SHMObjData **ppsmod
            );

        static
        void
        FreeSharedDataAreas(
            SHMPTR shmObjData
            );

        void
        PromoteSharedData(
            SHMPTR shmObjData,
            SHMObjData *psmod
            );

        bool
        DereferenceSharedData();

        virtual
        void
        AcquireObjectDestructionLock(
            CPalThread *pthr
            );

        virtual
        bool
        ReleaseObjectDestructionLock(
            CPalThread *pthr,
            bool fDestructionPending
            );
        
        virtual ~CSharedMemoryObject();

    public:

        //
        // Constructor used for new object
        //

        CSharedMemoryObject(
            CObjectType *pot,
            CRITICAL_SECTION *pcsObjListLock
            )
            :
            CPalObjectBase(pot),
            m_pcsObjListLock(pcsObjListLock),
            m_shmod(SHMNULL),
            m_pvSharedData(NULL),
            m_ObjectDomain(ProcessLocalObject),
            m_fSharedDataDereferenced(FALSE),
            m_fDeleteSharedData(FALSE)
        {
            InitializeListHead(&m_le);
        };

        //
        // Constructor used to import a shared object into this process. The
        // shared memory lock must be held when calling this contstructor
        //

        CSharedMemoryObject(
            CObjectType *pot,
            CRITICAL_SECTION *pcsObjListLock,
            SHMPTR shmSharedObjectData,
            SHMObjData *psmod,
            bool fAddRefSharedData
            )
            :
            CPalObjectBase(pot),
            m_pcsObjListLock(pcsObjListLock),
            m_shmod(shmSharedObjectData),
            m_pvSharedData(NULL),
            m_ObjectDomain(SharedObject),
            m_fSharedDataDereferenced(FALSE),
            m_fDeleteSharedData(FALSE)
        {
            InitializeListHead(&m_le);
            if (fAddRefSharedData)
            {
                psmod->lProcessRefCount += 1;
            }
        };

        virtual
        PAL_ERROR
        Initialize(
            CPalThread *pthr,
            CObjectAttributes *poa
            );

        virtual
        PAL_ERROR
        InitializeFromExistingSharedData(
            CPalThread *pthr,
            CObjectAttributes *poa
            );

        virtual
        PAL_ERROR
        EnsureObjectIsShared(
            CPalThread *pthr
            );

        void
        CleanupForProcessShutdown(
            CPalThread *pthr
            );

        SHMPTR
        GetShmObjData(
            void
            )
        {
            return m_shmod;
        };

        PLIST_ENTRY
        GetObjectListLink(
            void
            )
        {
            return &m_le;
        }

        //
        // Clients of this object -- in particular, CSharedMemoryObjectManager
        // -- can't use CONTAINING_RECORD directly, since they don't have
        // access to m_Link.
        //

        static
        CSharedMemoryObject*
        GetObjectFromListLink(PLIST_ENTRY pLink);

        //
        // IPalObject routines
        //
        
        virtual
        PAL_ERROR
        GetSharedData(
            CPalThread *pthr,
            LockType eLockRequest,
            IDataLock **ppDataLock,
            void **ppvSharedData
            );

        virtual
        PAL_ERROR
        GetSynchStateController(
            CPalThread *pthr,
            ISynchStateController **ppStateController
            );

        virtual
        PAL_ERROR
        GetSynchWaitController(
            CPalThread *pthr,
            ISynchWaitController **ppWaitController
            );

        virtual
        ObjectDomain
        GetObjectDomain(
            void
            );

        virtual
        PAL_ERROR
        GetObjectSynchData(
            VOID **ppvSynchData
            );

    };

    class CSharedMemoryWaitableObject : public CSharedMemoryObject
    {
        template <class T> friend void InternalDelete(T *p);
        
    protected:

        VOID *m_pvSynchData;

        virtual ~CSharedMemoryWaitableObject();
        
    public:

        CSharedMemoryWaitableObject(
            CObjectType *pot,
            CRITICAL_SECTION *pcsObjListLock
            )
            :
            CSharedMemoryObject(pot, pcsObjListLock),
            m_pvSynchData(NULL)
        {
        };

        //
        // Constructor used to import a shared object into this process. The
        // shared memory lock must be held when calling this contstructor
        //

        CSharedMemoryWaitableObject(
            CObjectType *pot,
            CRITICAL_SECTION *pcsObjListLock,
            SHMPTR shmSharedObjectData,
            SHMObjData *psmod,
            bool fAddRefSharedData
            )
            :
            CSharedMemoryObject(pot, pcsObjListLock, shmSharedObjectData, psmod, fAddRefSharedData),
            m_pvSynchData(psmod->pvSynchData)
        {
        };

        virtual
        PAL_ERROR
        Initialize(
            CPalThread *pthr,
            CObjectAttributes *poa
            );

        virtual
        PAL_ERROR
        EnsureObjectIsShared(
            CPalThread *pthr
            );

        //
        // IPalObject routines
        //

        virtual
        PAL_ERROR
        GetSynchStateController(
            CPalThread *pthr,
            ISynchStateController **ppStateController
            );

        virtual
        PAL_ERROR
        GetSynchWaitController(
            CPalThread *pthr,
            ISynchWaitController **ppWaitController
            );

        virtual
        PAL_ERROR
        GetObjectSynchData(
            VOID **ppvSynchData
            );
    };

}

#endif // _PAL_SHMOBJECT_HPP

