Coverage Report

Created: 2026-03-04 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/utils/collections/PubSub.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Publisher/Subscriber Pattern
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <winpr/config.h>
21
22
#include <winpr/crt.h>
23
24
#include <winpr/collections.h>
25
26
#include "../log.h"
27
#define TAG WINPR_TAG("pubsub")
28
29
/**
30
 * Events (C# Programming Guide)
31
 * http://msdn.microsoft.com/en-us/library/awbftdfh.aspx
32
 */
33
34
struct s_wPubSub
35
{
36
  CRITICAL_SECTION lock;
37
  BOOL synchronized;
38
39
  size_t size;
40
  size_t count;
41
  wEventType* events;
42
};
43
44
/**
45
 * Properties
46
 */
47
48
wEventType* PubSub_GetEventTypes(wPubSub* pubSub, size_t* count)
49
0
{
50
0
  WINPR_ASSERT(pubSub);
51
0
  if (count)
52
0
    *count = pubSub->count;
53
54
0
  return pubSub->events;
55
0
}
56
57
/**
58
 * Methods
59
 */
60
61
void PubSub_Lock(wPubSub* pubSub)
62
0
{
63
0
  WINPR_ASSERT(pubSub);
64
0
  if (pubSub->synchronized)
65
0
    EnterCriticalSection(&pubSub->lock);
66
0
}
67
68
void PubSub_Unlock(wPubSub* pubSub)
69
0
{
70
0
  WINPR_ASSERT(pubSub);
71
0
  if (pubSub->synchronized)
72
0
    LeaveCriticalSection(&pubSub->lock);
73
0
}
74
75
wEventType* PubSub_FindEventType(wPubSub* pubSub, const char* EventName)
76
0
{
77
0
  wEventType* event = nullptr;
78
79
0
  WINPR_ASSERT(pubSub);
80
0
  WINPR_ASSERT(EventName);
81
0
  for (size_t index = 0; index < pubSub->count; index++)
82
0
  {
83
0
    if (strcmp(pubSub->events[index].EventName, EventName) == 0)
84
0
    {
85
0
      event = &(pubSub->events[index]);
86
0
      break;
87
0
    }
88
0
  }
89
90
0
  return event;
91
0
}
92
93
void PubSub_AddEventTypes(wPubSub* pubSub, wEventType* events, size_t count)
94
0
{
95
0
  WINPR_ASSERT(pubSub);
96
0
  WINPR_ASSERT(events || (count == 0));
97
0
  if (pubSub->synchronized)
98
0
    PubSub_Lock(pubSub);
99
100
0
  const size_t required = pubSub->count + count;
101
0
  WINPR_ASSERT((required >= pubSub->count) && (required >= count));
102
103
0
  if (required >= pubSub->size)
104
0
  {
105
0
    size_t new_size = pubSub->size;
106
0
    do
107
0
    {
108
0
      WINPR_ASSERT(new_size <= SIZE_MAX - 128ull);
109
0
      new_size += 128ull;
110
0
    } while (new_size <= required);
111
112
0
    wEventType* new_event = (wEventType*)realloc(pubSub->events, new_size * sizeof(wEventType));
113
0
    if (!new_event)
114
0
      goto fail;
115
0
    pubSub->size = new_size;
116
0
    pubSub->events = new_event;
117
0
  }
118
119
0
  CopyMemory(&pubSub->events[pubSub->count], events, count * sizeof(wEventType));
120
0
  pubSub->count += count;
121
122
0
fail:
123
0
  if (pubSub->synchronized)
124
0
    PubSub_Unlock(pubSub);
125
0
}
126
127
int PubSub_Subscribe(wPubSub* pubSub, const char* EventName, ...)
128
0
{
129
0
  wEventType* event = nullptr;
130
0
  int status = -1;
131
0
  WINPR_ASSERT(pubSub);
132
133
0
  va_list ap = WINPR_C_ARRAY_INIT;
134
0
  va_start(ap, EventName);
135
0
  pEventHandler EventHandler = va_arg(ap, pEventHandler);
136
137
0
  if (pubSub->synchronized)
138
0
    PubSub_Lock(pubSub);
139
140
0
  event = PubSub_FindEventType(pubSub, EventName);
141
142
0
  if (event)
143
0
  {
144
0
    status = 0;
145
146
0
    if (event->EventHandlerCount < MAX_EVENT_HANDLERS)
147
0
      event->EventHandlers[event->EventHandlerCount++] = EventHandler;
148
0
    else
149
0
      status = -1;
150
0
  }
151
152
0
  if (pubSub->synchronized)
153
0
    PubSub_Unlock(pubSub);
154
155
0
  va_end(ap);
156
0
  return status;
157
0
}
158
159
int PubSub_Unsubscribe(wPubSub* pubSub, const char* EventName, ...)
160
0
{
161
0
  wEventType* event = nullptr;
162
0
  int status = -1;
163
0
  WINPR_ASSERT(pubSub);
164
0
  WINPR_ASSERT(EventName);
165
166
0
  va_list ap = WINPR_C_ARRAY_INIT;
167
0
  va_start(ap, EventName);
168
0
  pEventHandler EventHandler = va_arg(ap, pEventHandler);
169
170
0
  if (pubSub->synchronized)
171
0
    PubSub_Lock(pubSub);
172
173
0
  event = PubSub_FindEventType(pubSub, EventName);
174
175
0
  if (event)
176
0
  {
177
0
    status = 0;
178
179
0
    for (size_t index = 0; index < event->EventHandlerCount; index++)
180
0
    {
181
0
      if (event->EventHandlers[index] == EventHandler)
182
0
      {
183
0
        event->EventHandlers[index] = nullptr;
184
0
        event->EventHandlerCount--;
185
0
        MoveMemory((void*)&event->EventHandlers[index],
186
0
                   (void*)&event->EventHandlers[index + 1],
187
0
                   (MAX_EVENT_HANDLERS - index - 1) * sizeof(pEventHandler));
188
0
        status = 1;
189
0
      }
190
0
    }
191
0
  }
192
193
0
  if (pubSub->synchronized)
194
0
    PubSub_Unlock(pubSub);
195
196
0
  va_end(ap);
197
0
  return status;
198
0
}
199
200
int PubSub_OnEvent(wPubSub* pubSub, const char* EventName, void* context, const wEventArgs* e)
201
0
{
202
0
  WINPR_ASSERT(pubSub);
203
0
  WINPR_ASSERT(EventName);
204
0
  WINPR_ASSERT(e);
205
206
0
  if (!pubSub)
207
0
  {
208
0
    WLog_ERR(TAG, "pubSub(%s)=nullptr!", EventName);
209
0
    return -1;
210
0
  }
211
212
0
  if (pubSub->synchronized)
213
0
    PubSub_Lock(pubSub);
214
215
0
  int status = 0;
216
0
  wEventType* event = PubSub_FindEventType(pubSub, EventName);
217
0
  if (event)
218
0
  {
219
0
    for (size_t index = 0; index < event->EventHandlerCount; index++)
220
0
    {
221
0
      if (event->EventHandlers[index])
222
0
      {
223
0
        event->EventHandlers[index](context, e);
224
0
        status++;
225
0
      }
226
0
    }
227
0
  }
228
0
  if (pubSub->synchronized)
229
0
    PubSub_Unlock(pubSub);
230
231
0
  return status;
232
0
}
233
234
/**
235
 * Construction, Destruction
236
 */
237
238
wPubSub* PubSub_New(BOOL synchronized)
239
0
{
240
0
  wPubSub* pubSub = (wPubSub*)calloc(1, sizeof(wPubSub));
241
242
0
  if (!pubSub)
243
0
    return nullptr;
244
245
0
  pubSub->synchronized = synchronized;
246
247
0
  if (pubSub->synchronized && !InitializeCriticalSectionAndSpinCount(&pubSub->lock, 4000))
248
0
    goto fail;
249
250
0
  pubSub->count = 0;
251
0
  pubSub->size = 64;
252
253
0
  pubSub->events = (wEventType*)calloc(pubSub->size, sizeof(wEventType));
254
0
  if (!pubSub->events)
255
0
    goto fail;
256
257
0
  return pubSub;
258
0
fail:
259
0
  WINPR_PRAGMA_DIAG_PUSH
260
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
261
0
  PubSub_Free(pubSub);
262
0
  WINPR_PRAGMA_DIAG_POP
263
0
  return nullptr;
264
0
}
265
266
void PubSub_Free(wPubSub* pubSub)
267
0
{
268
0
  if (pubSub)
269
0
  {
270
0
    if (pubSub->synchronized)
271
0
      DeleteCriticalSection(&pubSub->lock);
272
273
0
    free(pubSub->events);
274
0
    free(pubSub);
275
0
  }
276
0
}