2
* Pure Data Packet system implementation: Packet Manager
3
* Copyright (c) by Tom Schouten <pdp@zzz.kotnet.org>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28
#include "pdp_packet.h"
32
#include "pdp_debug.h"
35
/* packet implementation. contains class and packet (instance) handling
37
some notes on packet operations.
38
copy ro/rw and unregister are relatively straightforward
39
packet creation can be done in 2 ways in this interface:
41
however, these methods should only be called by specific factory
42
methods, so the user should only create packets using pdp_factory_newpacket
44
reuse or create is thus the responsability of the factory methods for
45
each packet type (class) implementation
52
the packet pool methods are called within the pool locks. this probably
53
needs to change, because it will cause deadlocks for container packets (fobs) */
56
/* new implementation: probably just a minor adjustment: add the reuse fifo attached
57
to type desc symbol name
58
need to check and possibly eliminate hacks for non-pure packets
63
2. empty -> create packet+return (search array)
64
3. element -> check if type is correct, yes->pop+return, no->goto 1.
68
pdp_packet_mark_unused
70
1. check refcount. if > 1 dec + exit
71
2. if 1 put packet to sleep
73
4. add to reuse fifo (no fifo -> create)
75
pdp_packet_delete: analogous to mark_unused
76
pdp_packet_copy_ro/rw: analogous to new
82
#define PDP_INITIAL_POOL_SIZE 64
83
static int pdp_pool_size;
84
static t_pdp** pdp_pool;
86
/* mutex: protects the pool and reuse lists attached to symbols */
87
static pthread_mutex_t pdp_pool_mutex;
88
#define LOCK pthread_mutex_lock (&pdp_pool_mutex)
89
#define UNLOCK pthread_mutex_unlock (&pdp_pool_mutex)
91
/* the list of classes */
92
static t_pdp_list *class_list;
96
pdp_packet_print_debug(int packet)
98
t_pdp *h = pdp_packet_header(packet);
99
pdp_post("debug info for packet %d", packet);
101
pdp_post("invalid packet");
104
pdp_post ("\ttype: %d", h->type);
105
pdp_post ("\tdesc: %s", h->desc ? h->desc->s_name : "unknown");
106
pdp_post ("\tsize: %d", h->size);
107
pdp_post ("\tflags: %x", h->flags);
108
pdp_post ("\tusers: %d", h->users);
109
pdp_post ("\tclass: %x", h->theclass);
118
pdp_packet_setup(void)
121
pdp_pool_size = PDP_INITIAL_POOL_SIZE;
122
pdp_pool = (t_pdp **)pdp_alloc(PDP_INITIAL_POOL_SIZE * sizeof(t_pdp *));
123
bzero(pdp_pool, pdp_pool_size * sizeof(t_pdp *));
124
class_list = pdp_list_new(0);
125
pthread_mutex_init(&pdp_pool_mutex, NULL);
129
t_pdp_class *pdp_class_new(t_pdp_symbol *type, t_pdp_factory_method create){
130
t_pdp_class *c = (t_pdp_class *)pdp_alloc(sizeof(t_pdp_class));
131
memset(c, 0, sizeof(t_pdp_class));
133
c->type = type; // set type
134
pdp_list_add(class_list, a_pointer, (t_pdp_word)((void *)c));
138
/* the packet factory */
139
int pdp_factory_newpacket(t_pdp_symbol *type)
143
t_pdp_atom *a = class_list->first;
145
/* try to reuse first
146
THINK: should this be the responsability of the type specific constructors,
147
or should a packet allways be reusable (solution: depends on what the cleanup method returns??)
149
p = pdp_packet_reuse(type);
150
if (-1 != p) return p;
153
/* call class constructor */
155
c = (t_pdp_class *)(a->w.w_pointer);
156
if (c->type && pdp_type_description_match(type, c->type)){
157
//pdp_post("method %x, type %s", c->create, type->s_name);
158
return (c->create) ? (*c->create)(type) : -1;
166
_pdp_pool_expand_nolock(void){
169
/* double the size */
170
int new_pool_size = pdp_pool_size << 1;
171
t_pdp **new_pool = (t_pdp **)pdp_alloc(new_pool_size * sizeof(t_pdp *));
172
bzero(new_pool, new_pool_size * sizeof(t_pdp *));
173
memcpy(new_pool, pdp_pool, pdp_pool_size * sizeof(t_pdp *));
174
pdp_dealloc(pdp_pool);
176
pdp_pool_size = new_pool_size;
182
/* private _pdp_packet methods */
184
/* packets can only be created and destroyed using these 2 methods */
185
/* it updates the mem usage and total packet count */
188
_pdp_packet_dealloc_nolock(t_pdp *p)
195
_pdp_packet_alloc_nolock(unsigned int datatype, unsigned int datasize)
197
unsigned int totalsize = datasize + PDP_HEADER_SIZE;
198
t_pdp *p = (t_pdp *)pdp_alloc(totalsize);
200
memset(p, 0, PDP_HEADER_SIZE); //initialize header to 0
209
/* create a new packet and expand pool if necessary */
211
_pdp_packet_create_nolock(unsigned int datatype, unsigned int datasize)
215
for (; p < pdp_pool_size; p++){
217
/* found slot to store packet*/
218
t_pdp *header = _pdp_packet_alloc_nolock(datatype, datasize);
219
if (!header) return -1; // error allocating packet
220
pdp_pool[p] = header;
224
/* no slot found, expand pool */
225
_pdp_pool_expand_nolock();
231
pdp_packet_destroy(void)
234
/* dealloc all the data in object stack */
235
pdp_post("DEBUG: pdp_packet_destroy: clearing object pool.");
236
while ((i < pdp_pool_size) && (pdp_pool[i])) _pdp_packet_dealloc_nolock(pdp_pool[i++]);
246
/* public pool operations: have to be thread safe so each entry point
250
/* create a new packet.
251
this should only be used by type specific factory methods, and only if the
252
reuse method fails, since it will always create a new packet */
254
pdp_packet_create(unsigned int datatype, unsigned int datasize /*without header*/)
258
packet = _pdp_packet_create_nolock(datatype, datasize);
264
/* return a new packet.
265
it tries to reuse a packet based on
266
1. matching data size
267
2. abscence of destructor (which SHOULD mean there are no enclosed references)
269
it obviously can't use the reuse fifo tagged to a symbolic type description
271
ALWAYS USE pdp_packet_reuse BEFORE calling pdp_packet_new if possible
272
use both ONLY IN CONSTRUCTORS !!!
274
use pdp_packet_factory to create packets as a "user"
276
this is a summary of all internal packet creation mechanisms:
278
-> pdp_packet_reuse, which uses symbolic type descriptions, and should work for all packet types
279
it returns an initialized container (meta = correct, data = garbage)
281
-> pdp_packet_new, which only works for non-pure packets, and reuses packets based on data type
282
it returns a pure packet (meta + data = garbage)
284
-> pdp_packet_create, like pdp_packet_new, only it always creates a new packet
291
pdp_packet_new(unsigned int datatype, unsigned int datasize)
296
for (packet = 0; packet < pdp_pool_size; packet++){
297
header = pdp_pool[packet];
298
/* check data size */
300
&& header->users == 0 // must be unused
301
&& header->size == datasize + PDP_HEADER_SIZE // must be same size
302
&& !(header->theclass && header->theclass->cleanup)){ // must be pure packet (no destructor)
304
/* ok, got one. initialize */
305
memset(header, 0, PDP_HEADER_SIZE);
307
header->type = datatype;
308
header->size = datasize + PDP_HEADER_SIZE;
315
/* no usable non-pure packet found, create a new one */
318
return pdp_packet_create(datatype, datasize);
325
/* internal method to add a packet to a packet type
326
description symbol's unused packet fifo */
328
_pdp_packet_save_nolock(int packet)
330
t_pdp *header = pdp_packet_header(packet);
333
PDP_ASSERT(header->users == 0);
334
PDP_ASSERT(header->desc);
336
if (!s->s_reusefifo) s->s_reusefifo = pdp_list_new(0);
337
pdp_list_add(s->s_reusefifo, a_packet, (t_pdp_word)packet);
340
/* this will revive a packet matching a certain type description
341
no wildcards are allowed */
343
pdp_packet_reuse(t_pdp_symbol *type_description)
349
if (!type_description || !(l = type_description->s_reusefifo)) goto exit;
351
packet = pdp_list_pop(l).w_packet;
352
header = pdp_packet_header(packet);
354
/* check if reuse fifo is consistent (packet unused + correct type)
355
packet could be deleted and replaced with another one, or
356
revived without the index updated (it's a "hint cache") */
358
if (header->users == 0){
359
/* check if type matches */
360
if (pdp_type_description_match(header->desc, type_description)){
361
header->users++; // revive
364
/* if not, add the packet to the correct reuse fifo */
366
_pdp_packet_save_nolock(packet);
370
/* remove dangling refs */
377
if (header && header->theclass && header->theclass->wakeup){
378
header->theclass->wakeup(header); // revive if necessary
383
/* find all unused packets in pool, marked as used (to protect from other reapers)
384
and return them as a list. non-pure packets are not revived */
390
/* this returns a copy of a packet for read only access.
391
(increases refcount of the packet -> packet will become readonly if it was
392
writable, i.e. had rc=1 */
395
pdp_packet_copy_ro(int handle)
399
if (header = pdp_packet_header(handle)){
400
PDP_ASSERT(header->users); // consistency check
402
header->users++; // increment reference count
409
/* clone a packet: create a new packet with the same
410
type as the source packet */
413
pdp_packet_clone_rw(int handle)
419
if (header = pdp_packet_header(handle)){
420
/* consistency checks */
421
PDP_ASSERT(header->users);
422
PDP_ASSERT(header->desc);
424
/* first try to reuse old packet */
425
new_handle = pdp_packet_reuse(header->desc);
427
/* if this failed, create a new one using the central packet factory method */
428
if (-1 == new_handle) new_handle = pdp_factory_newpacket(header->desc);
430
/* if the factory method failed cline it manually */
431
if (-1 == new_handle) {
433
//pdp_post("WARNING: pdp_clone_rw: working around non-implemented factory method.");
434
new_handle = pdp_packet_new(header->type, header->size - PDP_HEADER_SIZE);
435
new_header = pdp_packet_header(new_handle);
437
memcpy(new_header, header, PDP_HEADER_SIZE);
445
/* return a copy of a packet (clone + copy data) */
447
pdp_packet_copy_rw(int handle)
449
t_pdp *header, *new_header;
452
if (!(header = pdp_packet_header(handle))) return -1;
454
/* check if we are allowed to copy */
455
if (header->flags & PDP_FLAG_DONOTCOPY) return -1;
457
/* get target packet */
458
new_handle = pdp_packet_clone_rw(handle);
459
if (-1 == new_handle) return -1;
460
new_header = pdp_packet_header(new_handle);
462
/* if there is a copy method, use that one */
463
if (header->theclass && header->theclass->copy){
464
header->theclass->copy(header, new_header);
467
/* otherwize copy the data verbatim */
469
memcpy(pdp_packet_data(new_handle),
470
pdp_packet_data(handle),
471
pdp_packet_data_size(handle));
479
/* decrement refcount */
480
void pdp_packet_mark_unused(int handle)
483
if (!(header = pdp_packet_header(handle))) return;
485
PDP_ASSERT(header->users); // consistency check
489
/* just decrement refcount */
490
if (header->users > 1){
494
/* put packet to sleep if refcount 1->0 */
496
if (header->theclass && header->theclass->sleep){
497
/* call sleep method (if any) outside of lock
498
while the packet is still alive, so it won't be
499
acclaimed by another thread */
501
header->theclass->sleep(header);
504
/* clear refcount & save in fifo for later use */
506
if (header->desc) // sleep could have destructed packet..
507
_pdp_packet_save_nolock(handle);
515
/* delete a packet. rc needs to be == 1 */
516
void pdp_packet_delete(int handle)
519
header = pdp_packet_header(handle);
521
PDP_ASSERT(header->users == 1); // consistency check
525
if (header->theclass && header->theclass->cleanup){
526
/* call cleanup method (if any) outside of lock
527
while the packet is still alive, so it won't be
528
acclaimed by another thread */
530
header->theclass->cleanup(header);
534
/* delete the packet */
535
pdp_pool[handle] = 0;
536
_pdp_packet_dealloc_nolock(header);
548
/* public data access methods */
551
pdp_packet_header(int handle)
553
if ((handle >= 0) && (handle < pdp_pool_size)) return pdp_pool[handle];
558
pdp_packet_subheader(int handle)
560
t_pdp* header = pdp_packet_header(handle);
561
if (!header) return 0;
562
return (void *)(&header->info.raw);
566
pdp_packet_data(int handle)
569
if ((handle >= 0) && (handle < pdp_pool_size))
571
h = pdp_pool[handle];
573
return (char *)(h) + PDP_HEADER_SIZE;
579
pdp_packet_data_size(int handle)
582
if ((handle >= 0) && (handle < pdp_pool_size))
584
h = pdp_pool[handle];
586
return h->size - PDP_HEADER_SIZE;
594
int pdp_packet_writable(int packet) /* returns true if packet is writable */
596
t_pdp *h = pdp_packet_header(packet);
598
return (h->users == 1);
601
void pdp_packet_replace_with_writable(int *packet) /* replaces a packet with a writable copy */
604
if (!pdp_packet_writable(*packet)){
605
new_p = pdp_packet_copy_rw(*packet);
606
pdp_packet_mark_unused(*packet);
615
pdp_pool_collect_garbage(void)
617
pdp_post("ERROR: garbage collector not implemented");
622
pdp_pool_set_max_mem_usage(int max)
624
pdp_post("ERROR: mem limit not implemented");