Revert r1404 and keep it on a development branch until it is fully tested.
[profile/ivi/pulseaudio-panda.git] / src / pulsecore / memblock.c
1 /* $Id$ */
2
3 /***
4   This file is part of PulseAudio.
5  
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10  
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   Lesser General Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with PulseAudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <assert.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include <pulse/xmalloc.h>
33
34 #include <pulsecore/shm.h>
35 #include <pulsecore/log.h>
36 #include <pulsecore/hashmap.h>
37
38 #include "memblock.h"
39
40 #define PA_MEMPOOL_SLOTS_MAX 128
41 #define PA_MEMPOOL_SLOT_SIZE (16*1024)
42
43 #define PA_MEMEXPORT_SLOTS_MAX 128
44
45 #define PA_MEMIMPORT_SLOTS_MAX 128
46 #define PA_MEMIMPORT_SEGMENTS_MAX 16
47
48 struct pa_memimport_segment {
49     pa_memimport *import;
50     pa_shm memory;
51     unsigned n_blocks;
52 };
53
54 struct pa_memimport {
55     pa_mempool *pool;
56     pa_hashmap *segments;
57     pa_hashmap *blocks;
58
59     /* Called whenever an imported memory block is no longer
60      * needed. */
61     pa_memimport_release_cb_t release_cb;
62     void *userdata;
63
64     PA_LLIST_FIELDS(pa_memimport);
65 };
66
67 struct memexport_slot {
68     PA_LLIST_FIELDS(struct memexport_slot);
69     pa_memblock *block;
70 };
71
72 struct pa_memexport {
73     pa_mempool *pool;
74     
75     struct memexport_slot slots[PA_MEMEXPORT_SLOTS_MAX];
76     PA_LLIST_HEAD(struct memexport_slot, free_slots);
77     PA_LLIST_HEAD(struct memexport_slot, used_slots);
78     unsigned n_init;
79
80     /* Called whenever a client from which we imported a memory block
81        which we in turn exported to another client dies and we need to
82        revoke the memory block accordingly */
83     pa_memexport_revoke_cb_t revoke_cb;
84     void *userdata;
85
86     PA_LLIST_FIELDS(pa_memexport);
87 };
88
89 struct mempool_slot {
90     PA_LLIST_FIELDS(struct mempool_slot);
91     /* the actual data follows immediately hereafter */
92 };
93
94 struct pa_mempool {
95     pa_shm memory;
96     size_t block_size;
97     unsigned n_blocks, n_init;
98
99     PA_LLIST_HEAD(pa_memimport, imports);
100     PA_LLIST_HEAD(pa_memexport, exports);
101
102     /* A list of free slots that may be reused */
103     PA_LLIST_HEAD(struct mempool_slot, free_slots);
104     
105     pa_mempool_stat stat;
106 };
107
108 static void segment_detach(pa_memimport_segment *seg);
109
110 static void stat_add(pa_memblock*b) {
111     assert(b);
112     assert(b->pool);
113
114     AO_fetch_and_add1_release_write(&b->pool->stat.n_allocated);
115     AO_fetch_and_add_release_write(&b->pool->stat.allocated_size, (AO_t) b->length);
116
117     AO_fetch_and_add1_release_write(&b->pool->stat.n_accumulated);
118     AO_fetch_and_add_release_write(&b->pool->stat.accumulated_size, (AO_t) b->length);
119
120     if (b->type == PA_MEMBLOCK_IMPORTED) {
121         AO_fetch_and_add1_release_write(&b->pool->stat.n_imported);
122         AO_fetch_and_add_release_write(&b->pool->stat.imported_size, (AO_t) b->length);
123     }
124
125     AO_fetch_and_add1_release_write(&b->pool->stat.n_allocated_by_type[b->type]);
126     AO_fetch_and_add1_release_write(&b->pool->stat.n_accumulated_by_type[b->type]);
127 }
128
129 static void stat_remove(pa_memblock *b) {
130     assert(b);
131     assert(b->pool);
132
133     assert(AO_load_acquire_read(&b->pool->stat.n_allocated) > 0);
134     assert(AO_load_acquire_read(&b->pool->stat.allocated_size) >= (AO_t) b->length);
135            
136     AO_fetch_and_sub1_release_write(&b->pool->stat.n_allocated);
137     AO_fetch_and_add_release_write(&b->pool->stat.allocated_size,  (AO_t) (-b->length));
138
139     if (b->type == PA_MEMBLOCK_IMPORTED) {
140         assert(AO_load_acquire_read(&b->pool->stat.n_imported) > 0);
141         assert(AO_load_acquire_read(&b->pool->stat.imported_size) >= (AO_t) b->length);
142         
143         AO_fetch_and_sub1_release_write(&b->pool->stat.n_imported);
144         AO_fetch_and_add_release_write(&b->pool->stat.imported_size, (AO_t)  (-b->length));
145     }
146
147     AO_fetch_and_sub1_release_write(&b->pool->stat.n_allocated_by_type[b->type]);
148 }
149
150 static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length);
151
152 pa_memblock *pa_memblock_new(pa_mempool *p, size_t length) {
153     pa_memblock *b;
154     
155     assert(p);
156     assert(length > 0);
157     
158     if (!(b = pa_memblock_new_pool(p, length)))
159         b = memblock_new_appended(p, length);
160
161     return b;
162 }
163
164 static pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) {
165     pa_memblock *b;
166
167     assert(p);
168     assert(length > 0);
169
170     b = pa_xmalloc(sizeof(pa_memblock) + length);
171     b->type = PA_MEMBLOCK_APPENDED;
172     b->read_only = 0;
173     PA_REFCNT_INIT(b);
174     b->length = length;
175     b->data = (uint8_t*) b + sizeof(pa_memblock);
176     b->pool = p;
177
178     stat_add(b);
179     return b;
180 }
181
182 static struct mempool_slot* mempool_allocate_slot(pa_mempool *p) {
183     struct mempool_slot *slot;
184     assert(p);
185
186     if (p->free_slots) {
187         slot = p->free_slots;
188         PA_LLIST_REMOVE(struct mempool_slot, p->free_slots, slot);
189     } else if (p->n_init < p->n_blocks)
190         slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * p->n_init++));
191     else {
192         pa_log_debug("Pool full");
193         AO_fetch_and_add1_release_write(&p->stat.n_pool_full);
194         return NULL;
195     }
196
197     return slot;
198 }
199
200 static void* mempool_slot_data(struct mempool_slot *slot) {
201     assert(slot);
202
203     return (uint8_t*) slot + sizeof(struct mempool_slot);
204 }
205
206 static unsigned mempool_slot_idx(pa_mempool *p, void *ptr) {
207     assert(p);
208     assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr);
209     assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size);
210
211     return ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size;
212 }
213
214 static struct mempool_slot* mempool_slot_by_ptr(pa_mempool *p, void *ptr) {
215     unsigned idx;
216
217     if ((idx = mempool_slot_idx(p, ptr)) == (unsigned) -1)
218         return NULL;
219
220     return (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (idx * p->block_size));
221 }
222
223 pa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) {
224     pa_memblock *b = NULL;
225     struct mempool_slot *slot;
226
227     assert(p);
228     assert(length > 0);
229
230     if (p->block_size - sizeof(struct mempool_slot) >= sizeof(pa_memblock) + length) {
231
232         if (!(slot = mempool_allocate_slot(p)))
233             return NULL;
234         
235         b = mempool_slot_data(slot);
236         b->type = PA_MEMBLOCK_POOL;
237         b->data = (uint8_t*) b + sizeof(pa_memblock);
238         
239     } else if (p->block_size - sizeof(struct mempool_slot) >= length) {
240
241         if (!(slot = mempool_allocate_slot(p)))
242             return NULL;
243         
244         b = pa_xnew(pa_memblock, 1);
245         b->type = PA_MEMBLOCK_POOL_EXTERNAL;
246         b->data = mempool_slot_data(slot);
247     } else {
248         pa_log_debug("Memory block too large for pool: %u > %u", length, p->block_size - sizeof(struct mempool_slot));
249         AO_fetch_and_add1_release_write(&p->stat.n_too_large_for_pool);
250         return NULL;
251     }
252
253     b->length = length;
254     b->read_only = 0;
255     PA_REFCNT_INIT(b);
256     b->pool = p;
257
258     stat_add(b);
259     return b;
260 }
261
262 pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int read_only) {
263     pa_memblock *b;
264
265     assert(p);
266     assert(d);
267     assert(length > 0);
268
269     b = pa_xnew(pa_memblock, 1);
270     b->type = PA_MEMBLOCK_FIXED;
271     b->read_only = read_only;
272     PA_REFCNT_INIT(b);
273     b->length = length;
274     b->data = d;
275     b->pool = p;
276
277     stat_add(b);
278     return b;
279 }
280
281 pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*free_cb)(void *p), int read_only) {
282     pa_memblock *b;
283
284     assert(p);
285     assert(d);
286     assert(length > 0);
287     assert(free_cb);
288     
289     b = pa_xnew(pa_memblock, 1);
290     b->type = PA_MEMBLOCK_USER;
291     b->read_only = read_only;
292     PA_REFCNT_INIT(b);
293     b->length = length;
294     b->data = d;
295     b->per_type.user.free_cb = free_cb;
296     b->pool = p;
297
298     stat_add(b);
299     return b;
300 }
301
302 pa_memblock* pa_memblock_ref(pa_memblock*b) {
303     assert(b);
304     assert(PA_REFCNT_VALUE(b) > 0);
305
306     PA_REFCNT_INC(b);
307     return b;
308 }
309
310 void pa_memblock_unref(pa_memblock*b) {
311     assert(b);
312     assert(PA_REFCNT_VALUE(b) > 0);
313
314     if (PA_REFCNT_DEC(b) > 0)
315         return;
316     
317     stat_remove(b);
318
319     switch (b->type) {
320         case PA_MEMBLOCK_USER :
321             assert(b->per_type.user.free_cb);
322             b->per_type.user.free_cb(b->data);
323
324             /* Fall through */
325
326         case PA_MEMBLOCK_FIXED:
327         case PA_MEMBLOCK_APPENDED :
328             pa_xfree(b);
329             break;
330
331         case PA_MEMBLOCK_IMPORTED : {
332             pa_memimport_segment *segment;
333
334             segment = b->per_type.imported.segment;
335             assert(segment);
336             assert(segment->import);
337             
338             pa_hashmap_remove(segment->import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id));
339             segment->import->release_cb(segment->import, b->per_type.imported.id, segment->import->userdata);
340
341             if (-- segment->n_blocks <= 0)
342                 segment_detach(segment);
343             
344             pa_xfree(b);
345             break;
346         }
347
348         case PA_MEMBLOCK_POOL_EXTERNAL:
349         case PA_MEMBLOCK_POOL: {
350             struct mempool_slot *slot;
351
352             slot = mempool_slot_by_ptr(b->pool, b->data);
353             assert(slot);
354             
355             PA_LLIST_PREPEND(struct mempool_slot, b->pool->free_slots, slot);
356             
357             if (b->type == PA_MEMBLOCK_POOL_EXTERNAL)
358                 pa_xfree(b);
359
360             break;
361         }
362
363         case PA_MEMBLOCK_TYPE_MAX:
364         default:
365             abort();
366     }
367 }
368
369 static void memblock_make_local(pa_memblock *b) {
370     assert(b);
371
372     AO_fetch_and_sub1_release_write(&b->pool->stat.n_allocated_by_type[b->type]);
373
374     if (b->length <= b->pool->block_size - sizeof(struct mempool_slot)) {
375         struct mempool_slot *slot;
376
377         if ((slot = mempool_allocate_slot(b->pool))) {
378             void *new_data;
379             /* We can move it into a local pool, perfect! */
380             
381             b->type = PA_MEMBLOCK_POOL_EXTERNAL;
382             b->read_only = 0;
383
384             new_data = mempool_slot_data(slot);
385             memcpy(new_data, b->data, b->length);
386             b->data = new_data;
387             goto finish;
388         }
389     }
390
391     /* Humm, not enough space in the pool, so lets allocate the memory with malloc() */
392     b->type = PA_MEMBLOCK_USER;
393     b->per_type.user.free_cb = pa_xfree;
394     b->read_only = 0;
395     b->data = pa_xmemdup(b->data, b->length);
396
397 finish:
398     AO_fetch_and_add1_release_write(&b->pool->stat.n_allocated_by_type[b->type]);
399     AO_fetch_and_add1_release_write(&b->pool->stat.n_accumulated_by_type[b->type]);
400 }
401
402 void pa_memblock_unref_fixed(pa_memblock *b) {
403     assert(b);
404     assert(PA_REFCNT_VALUE(b) > 0);
405     assert(b->type == PA_MEMBLOCK_FIXED);
406
407     if (PA_REFCNT_VALUE(b) > 1)
408         memblock_make_local(b);
409
410     pa_memblock_unref(b);
411 }
412
413 static void memblock_replace_import(pa_memblock *b) {
414     pa_memimport_segment *seg;
415     
416     assert(b);
417     assert(b->type == PA_MEMBLOCK_IMPORTED);
418
419     assert(AO_load_acquire_read(&b->pool->stat.n_imported) > 0);
420     assert(AO_load_acquire_read(&b->pool->stat.imported_size) >= (AO_t) b->length);
421     AO_fetch_and_sub1_release_write(&b->pool->stat.n_imported);
422     AO_fetch_and_add_release_write(&b->pool->stat.imported_size, (AO_t) - b->length);
423
424     seg = b->per_type.imported.segment;
425     assert(seg);
426     assert(seg->import);
427
428     pa_hashmap_remove(
429             seg->import->blocks,
430             PA_UINT32_TO_PTR(b->per_type.imported.id));
431
432     memblock_make_local(b);
433
434     if (-- seg->n_blocks <= 0)
435         segment_detach(seg);
436 }
437
438 pa_mempool* pa_mempool_new(int shared) {
439     size_t ps;
440     pa_mempool *p;
441
442     p = pa_xnew(pa_mempool, 1);
443
444 #ifdef HAVE_SYSCONF
445     ps = (size_t) sysconf(_SC_PAGESIZE);
446 #elif defined(PAGE_SIZE)
447         ps = (size_t) PAGE_SIZE;
448 #else
449         ps = 4096; /* Let's hope it's like x86. */
450 #endif
451
452     p->block_size = (PA_MEMPOOL_SLOT_SIZE/ps)*ps;
453
454     if (p->block_size < ps)
455         p->block_size = ps;
456     
457     p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
458
459     assert(p->block_size > sizeof(struct mempool_slot));
460
461     if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) {
462         pa_xfree(p);
463         return NULL;
464     }
465
466     p->n_init = 0;
467     
468     PA_LLIST_HEAD_INIT(pa_memimport, p->imports);
469     PA_LLIST_HEAD_INIT(pa_memexport, p->exports);
470     PA_LLIST_HEAD_INIT(struct mempool_slot, p->free_slots);
471
472     memset(&p->stat, 0, sizeof(p->stat));
473
474     return p;
475 }
476
477 void pa_mempool_free(pa_mempool *p) {
478     assert(p);
479
480     while (p->imports)
481         pa_memimport_free(p->imports);
482
483     while (p->exports)
484         pa_memexport_free(p->exports);
485
486     if (AO_load_acquire_read(&p->stat.n_allocated) > 0)
487         pa_log_warn("WARNING! Memory pool destroyed but not all memory blocks freed!");
488     
489     pa_shm_free(&p->memory);
490     pa_xfree(p);
491 }
492
493 const pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) {
494     assert(p);
495
496     return &p->stat;
497 }
498
499 void pa_mempool_vacuum(pa_mempool *p) {
500     struct mempool_slot *slot;
501     
502     assert(p);
503
504     for (slot = p->free_slots; slot; slot = slot->next)
505         pa_shm_punch(&p->memory, (uint8_t*) slot + sizeof(struct mempool_slot) - (uint8_t*) p->memory.ptr, p->block_size - sizeof(struct mempool_slot));
506 }
507
508 int pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) {
509     assert(p);
510
511     if (!p->memory.shared)
512         return -1;
513
514     *id = p->memory.id;
515     
516     return 0;
517 }
518
519 int pa_mempool_is_shared(pa_mempool *p) {
520     assert(p);
521
522     return !!p->memory.shared;
523 }
524
525 /* For recieving blocks from other nodes */
526 pa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata) {
527     pa_memimport *i;
528
529     assert(p);
530     assert(cb);
531     
532     i = pa_xnew(pa_memimport, 1);
533     i->pool = p;
534     i->segments = pa_hashmap_new(NULL, NULL);
535     i->blocks = pa_hashmap_new(NULL, NULL);
536     i->release_cb = cb;
537     i->userdata = userdata;
538     
539     PA_LLIST_PREPEND(pa_memimport, p->imports, i);
540     return i;
541 }
542
543 static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i);
544
545 static pa_memimport_segment* segment_attach(pa_memimport *i, uint32_t shm_id) {
546     pa_memimport_segment* seg;
547
548     if (pa_hashmap_size(i->segments) >= PA_MEMIMPORT_SEGMENTS_MAX)
549         return NULL;
550
551     seg = pa_xnew(pa_memimport_segment, 1);
552     
553     if (pa_shm_attach_ro(&seg->memory, shm_id) < 0) {
554         pa_xfree(seg);
555         return NULL;
556     }
557
558     seg->import = i;
559     seg->n_blocks = 0;
560     
561     pa_hashmap_put(i->segments, PA_UINT32_TO_PTR(shm_id), seg);
562     return seg;
563 }
564
565 static void segment_detach(pa_memimport_segment *seg) {
566     assert(seg);
567
568     pa_hashmap_remove(seg->import->segments, PA_UINT32_TO_PTR(seg->memory.id));
569     pa_shm_free(&seg->memory);
570     pa_xfree(seg);
571 }
572
573 void pa_memimport_free(pa_memimport *i) {
574     pa_memexport *e;
575     pa_memblock *b;
576     
577     assert(i);
578
579     /* If we've exported this block further we need to revoke that export */
580     for (e = i->pool->exports; e; e = e->next)
581         memexport_revoke_blocks(e, i);
582
583     while ((b = pa_hashmap_get_first(i->blocks)))
584         memblock_replace_import(b);
585
586     assert(pa_hashmap_size(i->segments) == 0);
587
588     pa_hashmap_free(i->blocks, NULL, NULL);
589     pa_hashmap_free(i->segments, NULL, NULL);
590     
591     PA_LLIST_REMOVE(pa_memimport, i->pool->imports, i);
592     pa_xfree(i);
593 }
594
595 pa_memblock* pa_memimport_get(pa_memimport *i, uint32_t block_id, uint32_t shm_id, size_t offset, size_t size) {
596     pa_memblock *b;
597     pa_memimport_segment *seg;
598     
599     assert(i);
600
601     if (pa_hashmap_size(i->blocks) >= PA_MEMIMPORT_SLOTS_MAX)
602         return NULL;
603
604     if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id)))) 
605         if (!(seg = segment_attach(i, shm_id)))
606             return NULL;
607
608     if (offset+size > seg->memory.size)
609         return NULL;
610     
611     b = pa_xnew(pa_memblock, 1);
612     b->type = PA_MEMBLOCK_IMPORTED;
613     b->read_only = 1;
614     PA_REFCNT_INIT(b);
615     b->length = size;
616     b->data = (uint8_t*) seg->memory.ptr + offset;
617     b->pool = i->pool;
618     b->per_type.imported.id = block_id;
619     b->per_type.imported.segment = seg;
620
621     pa_hashmap_put(i->blocks, PA_UINT32_TO_PTR(block_id), b);
622
623     seg->n_blocks++;
624     
625     stat_add(b);
626     
627     return b;
628 }
629
630 int pa_memimport_process_revoke(pa_memimport *i, uint32_t id) {
631     pa_memblock *b;
632     assert(i);
633
634     if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id))))
635         return -1;
636     
637     memblock_replace_import(b);
638     return 0;
639 }
640
641 /* For sending blocks to other nodes */
642 pa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void *userdata) {
643     pa_memexport *e;
644     
645     assert(p);
646     assert(cb);
647
648     if (!p->memory.shared)
649         return NULL;
650     
651     e = pa_xnew(pa_memexport, 1);
652     e->pool = p;
653     PA_LLIST_HEAD_INIT(struct memexport_slot, e->free_slots);
654     PA_LLIST_HEAD_INIT(struct memexport_slot, e->used_slots);
655     e->n_init = 0;
656     e->revoke_cb = cb;
657     e->userdata = userdata;
658     
659     PA_LLIST_PREPEND(pa_memexport, p->exports, e);
660     return e;
661 }
662
663 void pa_memexport_free(pa_memexport *e) {
664     assert(e);
665
666     while (e->used_slots)
667         pa_memexport_process_release(e, e->used_slots - e->slots);
668
669     PA_LLIST_REMOVE(pa_memexport, e->pool->exports, e);
670     pa_xfree(e);
671 }
672
673 int pa_memexport_process_release(pa_memexport *e, uint32_t id) {
674     assert(e);
675
676     if (id >= e->n_init)
677         return -1;
678
679     if (!e->slots[id].block)
680         return -1;
681
682 /*     pa_log("Processing release for %u", id); */
683
684     assert(AO_load_acquire_read(&e->pool->stat.n_exported) > 0);
685     assert(AO_load_acquire_read(&e->pool->stat.exported_size) >= (AO_t) e->slots[id].block->length);
686     
687     AO_fetch_and_sub1_release_write(&e->pool->stat.n_exported);
688     AO_fetch_and_add_release_write(&e->pool->stat.exported_size, (AO_t) -e->slots[id].block->length);
689     
690     pa_memblock_unref(e->slots[id].block);
691     e->slots[id].block = NULL;
692
693     PA_LLIST_REMOVE(struct memexport_slot, e->used_slots, &e->slots[id]);
694     PA_LLIST_PREPEND(struct memexport_slot, e->free_slots, &e->slots[id]);
695
696     return 0;
697 }
698
699 static void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) {
700     struct memexport_slot *slot, *next;
701     assert(e);
702     assert(i);
703
704     for (slot = e->used_slots; slot; slot = next) {
705         uint32_t idx;
706         next = slot->next;
707         
708         if (slot->block->type != PA_MEMBLOCK_IMPORTED ||
709             slot->block->per_type.imported.segment->import != i)
710             continue;
711
712         idx = slot - e->slots;
713         e->revoke_cb(e, idx, e->userdata);
714         pa_memexport_process_release(e, idx);
715     }
716 }
717
718 static pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *b) {
719     pa_memblock *n;
720
721     assert(p);
722     assert(b);
723     
724     if (b->type == PA_MEMBLOCK_IMPORTED ||
725         b->type == PA_MEMBLOCK_POOL ||
726         b->type == PA_MEMBLOCK_POOL_EXTERNAL) {
727         assert(b->pool == p);
728         return pa_memblock_ref(b);
729     }
730
731     if (!(n = pa_memblock_new_pool(p, b->length)))
732         return NULL;
733
734     memcpy(n->data, b->data, b->length);
735     return n;
736 }
737
738 int pa_memexport_put(pa_memexport *e, pa_memblock *b, uint32_t *block_id, uint32_t *shm_id, size_t *offset, size_t * size) {
739     pa_shm *memory;
740     struct memexport_slot *slot;
741     
742     assert(e);
743     assert(b);
744     assert(block_id);
745     assert(shm_id);
746     assert(offset);
747     assert(size);
748     assert(b->pool == e->pool);
749
750     if (!(b = memblock_shared_copy(e->pool, b)))
751         return -1;
752
753     if (e->free_slots) {
754         slot = e->free_slots;
755         PA_LLIST_REMOVE(struct memexport_slot, e->free_slots, slot);
756     } else if (e->n_init < PA_MEMEXPORT_SLOTS_MAX) {
757         slot = &e->slots[e->n_init++];
758     } else {
759         pa_memblock_unref(b);
760         return -1;
761     }
762
763     PA_LLIST_PREPEND(struct memexport_slot, e->used_slots, slot);
764     slot->block = b;
765     *block_id = slot - e->slots;
766
767 /*     pa_log("Got block id %u", *block_id); */
768
769     if (b->type == PA_MEMBLOCK_IMPORTED) {
770         assert(b->per_type.imported.segment);
771         memory = &b->per_type.imported.segment->memory;
772     } else {
773         assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL);
774         assert(b->pool);
775         memory = &b->pool->memory;
776     }
777         
778     assert(b->data >= memory->ptr);
779     assert((uint8_t*) b->data + b->length <= (uint8_t*) memory->ptr + memory->size);
780     
781     *shm_id = memory->id;
782     *offset = (uint8_t*) b->data - (uint8_t*) memory->ptr;
783     *size = b->length;
784
785     AO_fetch_and_add1_release_write(&e->pool->stat.n_exported);
786     AO_fetch_and_add_release_write(&e->pool->stat.exported_size, (AO_t) b->length);
787
788     return 0;
789 }