/* * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the License); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an AS IS BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Threading; using Tizen.Internals.Errors; using Native = Tizen.Multimedia.Interop.MediaPacket; namespace Tizen.Multimedia { /// /// Represents a packet for multimedia. /// /// 3 public abstract partial class MediaPacket : IDisposable { private readonly LockState _lock = new LockState(); /// /// Validates the current object is not locked. /// /// The MediaPacket has already been disposed of. /// The MediaPacket is in use by another module. private void ValidateNotLocked() { ValidateNotDisposed(); if (_lock.IsLocked) { throw new InvalidOperationException("Can't perform any writing operation." + "The packet is in use, internally."); } } /// /// Provides a thread-safe lock state controller. /// private sealed class LockState { const int LOCKED = 1; const int UNLOCKED = 0; private int _locked = UNLOCKED; internal void SetLock() { if (Interlocked.CompareExchange(ref _locked, LOCKED, UNLOCKED) == LOCKED) { throw new InvalidOperationException("The packet is already locked."); } } internal void SetUnlock() { if (Interlocked.CompareExchange(ref _locked, UNLOCKED, LOCKED) == UNLOCKED) { Debug.Fail("The packet to unlock is not locked. " + "There must be an error somewhere that a lock isn't disposed correctly."); } } internal bool IsLocked => Interlocked.CompareExchange(ref _locked, 0, 0) == LOCKED; } /// /// Provides a thread-safe lock controller. /// /// /// using (var lock = BaseMediaPacket.Lock(mediaPacket)) /// { /// .... /// } /// internal sealed class Lock : IDisposable { private readonly MediaPacket _packet; private readonly GCHandle _gcHandle; private int _lockCount; internal static Lock Get(MediaPacket packet) { Debug.Assert(packet != null); lock (packet._lock) { Lock lck = FromHandle(packet._handle) ?? new Lock(packet); lck._lockCount++; return lck; } } private Lock(MediaPacket packet) { Debug.Assert(packet != null, "The packet is null!"); packet.ValidateNotDisposed(); _packet = packet; _packet._lock.SetLock(); _gcHandle = GCHandle.Alloc(this); SetExtra(GCHandle.ToIntPtr(_gcHandle)); } internal static Lock FromHandle(IntPtr handle) { Debug.Assert(handle != IntPtr.Zero); IntPtr extra = GetExtra(handle); if (extra == IntPtr.Zero) { return null; } return (Lock)GCHandle.FromIntPtr(extra).Target; } private void SetExtra(IntPtr ptr) { int ret = Native.SetExtra(_packet._handle, ptr); MultimediaDebug.AssertNoError(ret); } private static IntPtr GetExtra(IntPtr handle) { int ret = Native.GetExtra(handle, out var value); MultimediaDebug.AssertNoError(ret); return value; } internal IntPtr GetHandle() => _packet.GetHandle(); internal MediaPacket MediaPacket => _packet; private bool _isDisposed = false; public void Dispose() { if (!_isDisposed) { lock (_packet._lock) { _lockCount--; if (_lockCount == 0) { SetExtra(IntPtr.Zero); if (_gcHandle.IsAllocated) { _gcHandle.Free(); } // We can assure that at this point '_packet' is always locked by this lock. _packet._lock.SetUnlock(); } } _isDisposed = true; } } } } }