5629119ee081d6de7557a04f4c759cb37389a1b4
[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     /// <since_tizen> 3 </since_tizen>
29     public abstract partial class MediaPacket : IDisposable
30     {
31         private readonly LockState _lock = new LockState();
32
33         /// <summary>
34         /// Validates the current object is not locked.
35         /// </summary>
36         /// <exception cref="ObjectDisposedException">The MediaPacket has already been disposed of.</exception>
37         /// <exception cref="InvalidOperationException">The MediaPacket is in use by another module.</exception>
38         private void ValidateNotLocked()
39         {
40             ValidateNotDisposed();
41
42             if (_lock.IsLocked)
43             {
44                 throw new InvalidOperationException("Can't perform any writing operation." +
45                     "The packet is in use, internally.");
46             }
47         }
48
49         /// <summary>
50         /// Provides a thread-safe lock state controller.
51         /// </summary>
52         private sealed class LockState
53         {
54             const int LOCKED = 1;
55             const int UNLOCKED = 0;
56
57             private int _locked = UNLOCKED;
58
59             internal void SetLock()
60             {
61                 if (Interlocked.CompareExchange(ref _locked, LOCKED, UNLOCKED) == LOCKED)
62                 {
63                     throw new InvalidOperationException("The packet is already locked.");
64                 }
65             }
66
67             internal void SetUnlock()
68             {
69                 if (Interlocked.CompareExchange(ref _locked, UNLOCKED, LOCKED) == UNLOCKED)
70                 {
71                     Debug.Fail("The packet to unlock is not locked. " +
72                         "There must be an error somewhere that a lock isn't disposed correctly.");
73                 }
74             }
75
76             internal bool IsLocked => Interlocked.CompareExchange(ref _locked, 0, 0) == LOCKED;
77         }
78
79         /// <summary>
80         /// Provides a thread-safe lock controller.
81         /// </summary>
82         /// <example>
83         /// using (var lock = BaseMediaPacket.Lock(mediaPacket))
84         /// {
85         ///     ....
86         /// }
87         /// </example>
88         internal sealed class Lock : IDisposable
89         {
90             private readonly MediaPacket _packet;
91             private readonly GCHandle _gcHandle;
92             private int _lockCount;
93
94             internal static Lock Get(MediaPacket packet)
95             {
96                 Debug.Assert(packet != null);
97
98                 lock (packet._lock)
99                 {
100                     Lock lck = FromHandle(packet._handle) ?? new Lock(packet);
101
102                     lck._lockCount++;
103
104                     return lck;
105                 }
106             }
107
108             private Lock(MediaPacket packet)
109             {
110                 Debug.Assert(packet != null, "The packet is null!");
111
112                 packet.ValidateNotDisposed();
113
114                 _packet = packet;
115
116                 _packet._lock.SetLock();
117
118                 _gcHandle = GCHandle.Alloc(this);
119
120                 SetExtra(GCHandle.ToIntPtr(_gcHandle));
121             }
122
123             internal static Lock FromHandle(IntPtr handle)
124             {
125                 Debug.Assert(handle != IntPtr.Zero);
126
127                 IntPtr extra = GetExtra(handle);
128
129                 if (extra == IntPtr.Zero)
130                 {
131                     return null;
132                 }
133
134                 return (Lock)GCHandle.FromIntPtr(extra).Target;
135             }
136
137             private void SetExtra(IntPtr ptr)
138             {
139                 int ret = Interop.MediaPacket.SetExtra(_packet._handle, ptr);
140
141                 MultimediaDebug.AssertNoError(ret);
142             }
143
144             private static IntPtr GetExtra(IntPtr handle)
145             {
146                 int ret = Interop.MediaPacket.GetExtra(handle, out var value);
147
148                 MultimediaDebug.AssertNoError(ret);
149
150                 return value;
151             }
152
153             internal IntPtr GetHandle() => _packet.GetHandle();
154
155             internal MediaPacket MediaPacket => _packet;
156
157             private bool _isDisposed = false;
158
159             public void Dispose()
160             {
161                 if (!_isDisposed)
162                 {
163                     lock (_packet._lock)
164                     {
165                         _lockCount--;
166
167                         if (_lockCount == 0)
168                         {
169                             SetExtra(IntPtr.Zero);
170
171                             if (_gcHandle.IsAllocated)
172                             {
173                                 _gcHandle.Free();
174                             }
175
176                             // We can assure that at this point '_packet' is always locked by this lock.
177                             _packet._lock.SetUnlock();
178                         }
179                     }
180
181                     _isDisposed = true;
182                 }
183             }
184         }
185     }
186 }