Release 4.0.0-preview1-00201
[platform/core/csapi/tizenfx.git] / src / Tizen.Multimedia / MediaTool / MediaPacket.Lock.cs
1 /*
2  * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 using System;
18 using System.Diagnostics;
19 using System.Runtime.InteropServices;
20 using System.Threading;
21 using Tizen.Internals.Errors;
22
23 namespace Tizen.Multimedia
24 {
25     /// <summary>
26     /// Represents a packet for multimedia.
27     /// </summary>
28     public abstract partial class MediaPacket : IDisposable
29     {
30         private readonly LockState _lock = new LockState();
31
32         /// <summary>
33         /// Validates the current object is not locked.
34         /// </summary>
35         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
36         /// <exception cref="InvalidOperationException">The MediaPacket is in use by another module.</exception>
37         private void ValidateNotLocked()
38         {
39             ValidateNotDisposed();
40
41             if (_lock.IsLocked)
42             {
43                 throw new InvalidOperationException("Can't perform any writing operation." +
44                     "The packet is in use, internally.");
45             }
46         }
47
48         /// <summary>
49         /// Provides a thread-safe lock state controller.
50         /// </summary>
51         private sealed class LockState
52         {
53             const int LOCKED = 1;
54             const int UNLOCKED = 0;
55
56             private int _locked = UNLOCKED;
57
58             internal void SetLock()
59             {
60                 if (Interlocked.CompareExchange(ref _locked, LOCKED, UNLOCKED) == LOCKED)
61                 {
62                     throw new InvalidOperationException("The packet is already locked.");
63                 }
64             }
65
66             internal void SetUnlock()
67             {
68                 if (Interlocked.CompareExchange(ref _locked, UNLOCKED, LOCKED) == UNLOCKED)
69                 {
70                     Debug.Fail("The packet to unlock is not locked. " +
71                         "There must be an error somewhere that a lock isn't disposed correctly.");
72                 }
73             }
74
75             internal bool IsLocked
76             {
77                 get
78                 {
79                     return Interlocked.CompareExchange(ref _locked, 0, 0) == LOCKED;
80                 }
81             }
82         }
83
84         /// <summary>
85         /// Provides a thread-safe lock controller.
86         /// </summary>
87         /// <example>
88         /// using (var lock = BaseMediaPacket.Lock(mediaPacket))
89         /// {
90         ///     ....
91         /// }
92         /// </example>
93         internal sealed class Lock : IDisposable
94         {
95             private readonly MediaPacket _packet;
96             private readonly GCHandle _gcHandle;
97             private int _lockCount;
98
99             internal static Lock Get(MediaPacket packet)
100             {
101                 Debug.Assert(packet != null);
102
103                 lock (packet)
104                 {
105                     Lock lck = FromHandle(packet._handle);
106
107                     if (lck == null)
108                     {
109                         lck = new Lock(packet);
110                     }
111
112                     lck._lockCount++;
113
114                     return lck;
115                 }
116             }
117
118             private Lock(MediaPacket packet)
119             {
120                 Debug.Assert(packet != null, "The packet is null!");
121
122                 packet.ValidateNotDisposed();
123
124                 _packet = packet;
125
126                 _packet._lock.SetLock();
127
128                 _gcHandle = GCHandle.Alloc(this);
129
130                 SetExtra(GCHandle.ToIntPtr(_gcHandle));
131             }
132
133             internal static Lock FromHandle(IntPtr handle)
134             {
135                 Debug.Assert(handle != IntPtr.Zero);
136
137                 IntPtr extra = GetExtra(handle);
138
139                 if (extra == IntPtr.Zero)
140                 {
141                     return null;
142                 }
143
144                 return (Lock)GCHandle.FromIntPtr(extra).Target;
145             }
146
147             private void SetExtra(IntPtr ptr)
148             {
149                 int ret = Interop.MediaPacket.SetExtra(_packet._handle, ptr);
150
151                 MultimediaDebug.AssertNoError(ret);
152             }
153
154             private static IntPtr GetExtra(IntPtr handle)
155             {
156                 IntPtr value;
157
158                 int ret = Interop.MediaPacket.GetExtra(handle, out value);
159
160                 MultimediaDebug.AssertNoError(ret);
161
162                 return value;
163             }
164
165             internal IntPtr GetHandle()
166             {
167                 return _packet.GetHandle();
168             }
169
170             internal MediaPacket MediaPacket
171             {
172                 get
173                 {
174                     return _packet;
175                 }
176             }
177
178             private bool _isDisposed = false;
179
180             public void Dispose()
181             {
182                 if (!_isDisposed)
183                 {
184                     lock (_packet)
185                     {
186                         _lockCount--;
187
188                         if (_lockCount == 0)
189                         {
190                             SetExtra(IntPtr.Zero);
191
192                             if (_gcHandle.IsAllocated)
193                             {
194                                 _gcHandle.Free();
195                             }
196
197                             //We can assure that at this point '_packet' is always locked by this lock.
198                             _packet._lock.SetUnlock();
199                         }
200                     }
201
202                     _isDisposed = true;
203                 }
204             }
205         }
206     }
207 }