~diwic/ubuntu/lucid/pulseaudio/bugfixes

« back to all changes in this revision

Viewing changes to src/pulsecore/memblock.c

  • Committer: Bazaar Package Importer
  • Author(s): Luke Yelavich
  • Date: 2008-11-04 15:46:00 UTC
  • mfrom: (1.2.1 upstream) (1.1.6 lenny)
  • Revision ID: james.westby@ubuntu.com-20081104154600-hlzknpcazaam0nxm
Tags: 0.9.13-1ubuntu1
* Merge from Debian unstable, remaining changes:
  - Don't build against, and create jack package. Jack is not in main.
  - Remove --disable-per-user-esound-socket from configure flags, as we still
    want per user esound sockets.
  - Remove stop links from rc0 and rc6.
  - Change default resample algorithm and bubffer size.
  - Add alsa configuration files to route alsa applications via pulseaudio.
  - Move libasound2-plugins from Recommends to Depends.
* debian/pulseaudio.preinst: When upgrading from intrepid, remove
  /etc/X11/Xsession.d/70pulseaudio, as this was used to minimize a race
  condition when starting GNOME in intrepid. This race should not exist in
  jaunty once libcanberra is built to use pulseaudio as a backend.
* Do not spawn a pulseaudio server if clients fail to find a running server.
* Remove explicit version dependency for libspeex-dev to allow the package
  to be built for now.
* Regenerate autotools files to work with Ubuntu's newer libtool/libltdl.
* debian/control: libpulsecore5 -> libpulsecore8 to match the library
  soname.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: memblock.c 1971 2007-10-28 19:13:50Z lennart $ */
2
 
 
3
1
/***
4
2
  This file is part of PulseAudio.
5
3
 
33
31
#include <signal.h>
34
32
#include <errno.h>
35
33
 
 
34
#ifdef HAVE_VALGRIND_MEMCHECK_H
 
35
#include <valgrind/memcheck.h>
 
36
#endif
 
37
 
36
38
#include <pulse/xmalloc.h>
37
39
#include <pulse/def.h>
38
40
 
46
48
 
47
49
#include "memblock.h"
48
50
 
49
 
#define PA_MEMPOOL_SLOTS_MAX 128
50
 
#define PA_MEMPOOL_SLOT_SIZE (16*1024)
 
51
/* We can allocate 64*1024*1024 bytes at maximum. That's 64MB. Please
 
52
 * note that the footprint is usually much smaller, since the data is
 
53
 * stored in SHM and our OS does not commit the memory before we use
 
54
 * it for the first time. */
 
55
#define PA_MEMPOOL_SLOTS_MAX 1024
 
56
#define PA_MEMPOOL_SLOT_SIZE (64*1024)
51
57
 
52
58
#define PA_MEMEXPORT_SLOTS_MAX 128
53
59
 
59
65
    pa_mempool *pool;
60
66
 
61
67
    pa_memblock_type_t type;
62
 
    int read_only; /* boolean */
 
68
 
 
69
    pa_bool_t read_only:1;
 
70
    pa_bool_t is_silence:1;
63
71
 
64
72
    pa_atomic_ptr_t data;
65
73
    size_t length;
125
133
    PA_LLIST_FIELDS(pa_memexport);
126
134
};
127
135
 
128
 
struct mempool_slot {
129
 
    PA_LLIST_FIELDS(struct mempool_slot);
130
 
    /* the actual data follows immediately hereafter */
131
 
};
132
 
 
133
136
struct pa_mempool {
134
137
    pa_semaphore *semaphore;
135
138
    pa_mutex *mutex;
159
162
    pa_assert(b->pool);
160
163
 
161
164
    pa_atomic_inc(&b->pool->stat.n_allocated);
162
 
    pa_atomic_add(&b->pool->stat.allocated_size, b->length);
 
165
    pa_atomic_add(&b->pool->stat.allocated_size, (int) b->length);
163
166
 
164
167
    pa_atomic_inc(&b->pool->stat.n_accumulated);
165
 
    pa_atomic_add(&b->pool->stat.accumulated_size, b->length);
 
168
    pa_atomic_add(&b->pool->stat.accumulated_size, (int) b->length);
166
169
 
167
170
    if (b->type == PA_MEMBLOCK_IMPORTED) {
168
171
        pa_atomic_inc(&b->pool->stat.n_imported);
169
 
        pa_atomic_add(&b->pool->stat.imported_size, b->length);
 
172
        pa_atomic_add(&b->pool->stat.imported_size, (int) b->length);
170
173
    }
171
174
 
172
175
    pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
182
185
    pa_assert(pa_atomic_load(&b->pool->stat.allocated_size) >= (int) b->length);
183
186
 
184
187
    pa_atomic_dec(&b->pool->stat.n_allocated);
185
 
    pa_atomic_sub(&b->pool->stat.allocated_size,  b->length);
 
188
    pa_atomic_sub(&b->pool->stat.allocated_size, (int) b->length);
186
189
 
187
190
    if (b->type == PA_MEMBLOCK_IMPORTED) {
188
191
        pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
189
192
        pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
190
193
 
191
194
        pa_atomic_dec(&b->pool->stat.n_imported);
192
 
        pa_atomic_sub(&b->pool->stat.imported_size, b->length);
 
195
        pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);
193
196
    }
194
197
 
195
198
    pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
202
205
    pa_memblock *b;
203
206
 
204
207
    pa_assert(p);
205
 
    pa_assert(length > 0);
 
208
    pa_assert(length);
206
209
 
207
210
    if (!(b = pa_memblock_new_pool(p, length)))
208
211
        b = memblock_new_appended(p, length);
215
218
    pa_memblock *b;
216
219
 
217
220
    pa_assert(p);
218
 
    pa_assert(length > 0);
 
221
    pa_assert(length);
219
222
 
220
223
    /* If -1 is passed as length we choose the size for the caller. */
221
224
 
222
225
    if (length == (size_t) -1)
223
 
        length = p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) - PA_ALIGN(sizeof(pa_memblock));
 
226
        length = p->block_size - PA_ALIGN(sizeof(pa_memblock));
224
227
 
225
228
    b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length);
226
229
    PA_REFCNT_INIT(b);
227
230
    b->pool = p;
228
231
    b->type = PA_MEMBLOCK_APPENDED;
229
 
    b->read_only = 0;
 
232
    b->read_only = b->is_silence = FALSE;
230
233
    pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
231
234
    b->length = length;
232
235
    pa_atomic_store(&b->n_acquired, 0);
249
252
        if ((unsigned) (idx = pa_atomic_inc(&p->n_init)) >= p->n_blocks)
250
253
            pa_atomic_dec(&p->n_init);
251
254
        else
252
 
            slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * idx));
 
255
            slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) idx));
253
256
 
254
257
        if (!slot) {
255
 
            pa_log_debug("Pool full");
 
258
            pa_log_info("Pool full");
256
259
            pa_atomic_inc(&p->stat.n_pool_full);
257
260
            return NULL;
258
261
        }
259
262
    }
260
263
 
 
264
/* #ifdef HAVE_VALGRIND_MEMCHECK_H */
 
265
/*     if (PA_UNLIKELY(pa_in_valgrind())) { */
 
266
/*         VALGRIND_MALLOCLIKE_BLOCK(slot, p->block_size, 0, 0); */
 
267
/*     } */
 
268
/* #endif */
 
269
 
261
270
    return slot;
262
271
}
263
272
 
264
 
/* No lock necessary */
265
 
static void* mempool_slot_data(struct mempool_slot *slot) {
266
 
    pa_assert(slot);
267
 
 
268
 
    return (uint8_t*) slot + PA_ALIGN(sizeof(struct mempool_slot));
 
273
/* No lock necessary, totally redundant anyway */
 
274
static inline void* mempool_slot_data(struct mempool_slot *slot) {
 
275
    return slot;
269
276
}
270
277
 
271
278
/* No lock necessary */
275
282
    pa_assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr);
276
283
    pa_assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size);
277
284
 
278
 
    return ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size;
 
285
    return (unsigned) ((size_t) ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size);
279
286
}
280
287
 
281
288
/* No lock necessary */
294
301
    struct mempool_slot *slot;
295
302
 
296
303
    pa_assert(p);
297
 
    pa_assert(length > 0);
 
304
    pa_assert(length);
298
305
 
299
306
    /* If -1 is passed as length we choose the size for the caller: we
300
307
     * take the largest size that fits in one of our slots. */
302
309
    if (length == (size_t) -1)
303
310
        length = pa_mempool_block_size_max(p);
304
311
 
305
 
    if (p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) >= PA_ALIGN(sizeof(pa_memblock)) + length) {
 
312
    if (p->block_size >= PA_ALIGN(sizeof(pa_memblock)) + length) {
306
313
 
307
314
        if (!(slot = mempool_allocate_slot(p)))
308
315
            return NULL;
311
318
        b->type = PA_MEMBLOCK_POOL;
312
319
        pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock)));
313
320
 
314
 
    } else if (p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) >= length) {
 
321
    } else if (p->block_size >= length) {
315
322
 
316
323
        if (!(slot = mempool_allocate_slot(p)))
317
324
            return NULL;
323
330
        pa_atomic_ptr_store(&b->data, mempool_slot_data(slot));
324
331
 
325
332
    } else {
326
 
        pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) (p->block_size - PA_ALIGN(sizeof(struct mempool_slot))));
 
333
        pa_log_debug("Memory block too large for pool: %lu > %lu", (unsigned long) length, (unsigned long) p->block_size);
327
334
        pa_atomic_inc(&p->stat.n_too_large_for_pool);
328
335
        return NULL;
329
336
    }
330
337
 
331
338
    PA_REFCNT_INIT(b);
332
339
    b->pool = p;
333
 
    b->read_only = 0;
 
340
    b->read_only = b->is_silence = FALSE;
334
341
    b->length = length;
335
342
    pa_atomic_store(&b->n_acquired, 0);
336
343
    pa_atomic_store(&b->please_signal, 0);
340
347
}
341
348
 
342
349
/* No lock necessary */
343
 
pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, int read_only) {
 
350
pa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, pa_bool_t read_only) {
344
351
    pa_memblock *b;
345
352
 
346
353
    pa_assert(p);
347
354
    pa_assert(d);
348
355
    pa_assert(length != (size_t) -1);
349
 
    pa_assert(length > 0);
 
356
    pa_assert(length);
350
357
 
351
358
    if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks))))
352
359
        b = pa_xnew(pa_memblock, 1);
354
361
    b->pool = p;
355
362
    b->type = PA_MEMBLOCK_FIXED;
356
363
    b->read_only = read_only;
 
364
    b->is_silence = FALSE;
357
365
    pa_atomic_ptr_store(&b->data, d);
358
366
    b->length = length;
359
367
    pa_atomic_store(&b->n_acquired, 0);
364
372
}
365
373
 
366
374
/* No lock necessary */
367
 
pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, void (*free_cb)(void *p), int read_only) {
 
375
pa_memblock *pa_memblock_new_user(pa_mempool *p, void *d, size_t length, pa_free_cb_t free_cb, pa_bool_t read_only) {
368
376
    pa_memblock *b;
369
377
 
370
378
    pa_assert(p);
371
379
    pa_assert(d);
372
 
    pa_assert(length > 0);
 
380
    pa_assert(length);
373
381
    pa_assert(length != (size_t) -1);
374
382
    pa_assert(free_cb);
375
383
 
379
387
    b->pool = p;
380
388
    b->type = PA_MEMBLOCK_USER;
381
389
    b->read_only = read_only;
 
390
    b->is_silence = FALSE;
382
391
    pa_atomic_ptr_store(&b->data, d);
383
392
    b->length = length;
384
393
    pa_atomic_store(&b->n_acquired, 0);
391
400
}
392
401
 
393
402
/* No lock necessary */
394
 
int pa_memblock_is_read_only(pa_memblock *b) {
 
403
pa_bool_t pa_memblock_is_read_only(pa_memblock *b) {
395
404
    pa_assert(b);
396
405
    pa_assert(PA_REFCNT_VALUE(b) > 0);
397
406
 
399
408
}
400
409
 
401
410
/* No lock necessary */
402
 
int pa_memblock_ref_is_one(pa_memblock *b) {
 
411
pa_bool_t pa_memblock_is_silence(pa_memblock *b) {
 
412
    pa_assert(b);
 
413
    pa_assert(PA_REFCNT_VALUE(b) > 0);
 
414
 
 
415
    return b->is_silence;
 
416
}
 
417
 
 
418
/* No lock necessary */
 
419
void pa_memblock_set_is_silence(pa_memblock *b, pa_bool_t v) {
 
420
    pa_assert(b);
 
421
    pa_assert(PA_REFCNT_VALUE(b) > 0);
 
422
 
 
423
    b->is_silence = v;
 
424
}
 
425
 
 
426
/* No lock necessary */
 
427
pa_bool_t pa_memblock_ref_is_one(pa_memblock *b) {
403
428
    int r;
404
 
 
405
429
    pa_assert(b);
406
430
 
407
 
    r = PA_REFCNT_VALUE(b);
408
 
    pa_assert(r > 0);
 
431
    pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0);
409
432
 
410
433
    return r == 1;
411
434
}
506
529
        case PA_MEMBLOCK_POOL_EXTERNAL:
507
530
        case PA_MEMBLOCK_POOL: {
508
531
            struct mempool_slot *slot;
509
 
            int call_free;
 
532
            pa_bool_t call_free;
510
533
 
511
534
            slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data));
512
535
            pa_assert(slot);
513
536
 
514
537
            call_free = b->type == PA_MEMBLOCK_POOL_EXTERNAL;
515
538
 
 
539
/* #ifdef HAVE_VALGRIND_MEMCHECK_H */
 
540
/*             if (PA_UNLIKELY(pa_in_valgrind())) { */
 
541
/*                 VALGRIND_FREELIKE_BLOCK(slot, b->pool->block_size); */
 
542
/*             } */
 
543
/* #endif */
 
544
 
516
545
            /* The free list dimensions should easily allow all slots
517
546
             * to fit in, hence try harder if pushing this slot into
518
547
             * the free list fails */
567
596
 
568
597
    pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]);
569
598
 
570
 
    if (b->length <= b->pool->block_size - PA_ALIGN(sizeof(struct mempool_slot))) {
 
599
    if (b->length <= b->pool->block_size) {
571
600
        struct mempool_slot *slot;
572
601
 
573
602
        if ((slot = mempool_allocate_slot(b->pool))) {
579
608
            pa_atomic_ptr_store(&b->data, new_data);
580
609
 
581
610
            b->type = PA_MEMBLOCK_POOL_EXTERNAL;
582
 
            b->read_only = 0;
 
611
            b->read_only = FALSE;
583
612
 
584
613
            goto finish;
585
614
        }
590
619
    pa_atomic_ptr_store(&b->data, pa_xmemdup(pa_atomic_ptr_load(&b->data), b->length));
591
620
 
592
621
    b->type = PA_MEMBLOCK_USER;
593
 
    b->read_only = 0;
 
622
    b->read_only = FALSE;
594
623
 
595
624
finish:
596
625
    pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]);
634
663
    pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0);
635
664
    pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length);
636
665
    pa_atomic_dec(&b->pool->stat.n_imported);
637
 
    pa_atomic_sub(&b->pool->stat.imported_size, b->length);
 
666
    pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length);
638
667
 
639
668
    seg = b->per_type.imported.segment;
640
669
    pa_assert(seg);
655
684
        pa_mutex_unlock(seg->import->mutex);
656
685
}
657
686
 
658
 
pa_mempool* pa_mempool_new(int shared) {
 
687
pa_mempool* pa_mempool_new(pa_bool_t shared, size_t size) {
659
688
    pa_mempool *p;
 
689
    char t1[64], t2[64];
660
690
 
661
691
    p = pa_xnew(pa_mempool, 1);
662
692
 
667
697
    if (p->block_size < PA_PAGE_SIZE)
668
698
        p->block_size = PA_PAGE_SIZE;
669
699
 
670
 
    p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
 
700
    if (size <= 0)
 
701
        p->n_blocks = PA_MEMPOOL_SLOTS_MAX;
 
702
    else {
 
703
        p->n_blocks = (unsigned) (size / p->block_size);
671
704
 
672
 
    pa_assert(p->block_size > PA_ALIGN(sizeof(struct mempool_slot)));
 
705
        if (p->n_blocks < 2)
 
706
            p->n_blocks = 2;
 
707
    }
673
708
 
674
709
    if (pa_shm_create_rw(&p->memory, p->n_blocks * p->block_size, shared, 0700) < 0) {
675
710
        pa_xfree(p);
676
711
        return NULL;
677
712
    }
678
713
 
 
714
    pa_log_debug("Using %s memory pool with %u slots of size %s each, total size is %s",
 
715
                 p->memory.shared ? "shared" : "private",
 
716
                 p->n_blocks,
 
717
                 pa_bytes_snprint(t1, sizeof(t1), (unsigned) p->block_size),
 
718
                 pa_bytes_snprint(t2, sizeof(t2), (unsigned) (p->n_blocks * p->block_size)));
 
719
 
679
720
    memset(&p->stat, 0, sizeof(p->stat));
680
721
    pa_atomic_store(&p->n_init, 0);
681
722
 
682
723
    PA_LLIST_HEAD_INIT(pa_memimport, p->imports);
683
724
    PA_LLIST_HEAD_INIT(pa_memexport, p->exports);
684
725
 
685
 
    p->free_slots = pa_flist_new(p->n_blocks*2);
 
726
    p->free_slots = pa_flist_new(p->n_blocks);
686
727
 
687
728
    return p;
688
729
}
726
767
size_t pa_mempool_block_size_max(pa_mempool *p) {
727
768
    pa_assert(p);
728
769
 
729
 
    return p->block_size - PA_ALIGN(sizeof(struct mempool_slot)) - PA_ALIGN(sizeof(pa_memblock));
 
770
    return p->block_size - PA_ALIGN(sizeof(pa_memblock));
730
771
}
731
772
 
732
773
/* No lock necessary */
736
777
 
737
778
    pa_assert(p);
738
779
 
739
 
    list = pa_flist_new(p->n_blocks*2);
 
780
    list = pa_flist_new(p->n_blocks);
740
781
 
741
782
    while ((slot = pa_flist_pop(p->free_slots)))
742
783
        while (pa_flist_push(list, slot) < 0)
743
784
            ;
744
785
 
745
786
    while ((slot = pa_flist_pop(list))) {
746
 
        pa_shm_punch(&p->memory,
747
 
                     (uint8_t*) slot - (uint8_t*) p->memory.ptr + PA_ALIGN(sizeof(struct mempool_slot)),
748
 
                     p->block_size - PA_ALIGN(sizeof(struct mempool_slot)));
 
787
        pa_shm_punch(&p->memory, (size_t) ((uint8_t*) slot - (uint8_t*) p->memory.ptr), p->block_size);
749
788
 
750
789
        while (pa_flist_push(p->free_slots, slot))
751
790
            ;
767
806
}
768
807
 
769
808
/* No lock necessary */
770
 
int pa_mempool_is_shared(pa_mempool *p) {
 
809
pa_bool_t pa_mempool_is_shared(pa_mempool *p) {
771
810
    pa_assert(p);
772
811
 
773
812
    return !!p->memory.shared;
836
875
 
837
876
    pa_mutex_lock(i->mutex);
838
877
 
839
 
    while ((b = pa_hashmap_get_first(i->blocks)))
 
878
    while ((b = pa_hashmap_first(i->blocks)))
840
879
        memblock_replace_import(b);
841
880
 
842
881
    pa_assert(pa_hashmap_size(i->segments) == 0);
886
925
    PA_REFCNT_INIT(b);
887
926
    b->pool = i->pool;
888
927
    b->type = PA_MEMBLOCK_IMPORTED;
889
 
    b->read_only = 1;
 
928
    b->read_only = TRUE;
 
929
    b->is_silence = FALSE;
890
930
    pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset);
891
931
    b->length = size;
892
932
    pa_atomic_store(&b->n_acquired, 0);
957
997
 
958
998
    pa_mutex_lock(e->mutex);
959
999
    while (e->used_slots)
960
 
        pa_memexport_process_release(e, e->used_slots - e->slots);
 
1000
        pa_memexport_process_release(e, (uint32_t) (e->used_slots - e->slots));
961
1001
    pa_mutex_unlock(e->mutex);
962
1002
 
963
1003
    pa_mutex_lock(e->pool->mutex);
996
1036
    pa_assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) b->length);
997
1037
 
998
1038
    pa_atomic_dec(&e->pool->stat.n_exported);
999
 
    pa_atomic_sub(&e->pool->stat.exported_size, b->length);
 
1039
    pa_atomic_sub(&e->pool->stat.exported_size, (int) b->length);
1000
1040
 
1001
1041
    pa_memblock_unref(b);
1002
1042
 
1024
1064
            slot->block->per_type.imported.segment->import != i)
1025
1065
            continue;
1026
1066
 
1027
 
        idx = slot - e->slots;
 
1067
        idx = (uint32_t) (slot - e->slots);
1028
1068
        e->revoke_cb(e, idx, e->userdata);
1029
1069
        pa_memexport_process_release(e, idx);
1030
1070
    }
1085
1125
 
1086
1126
    PA_LLIST_PREPEND(struct memexport_slot, e->used_slots, slot);
1087
1127
    slot->block = b;
1088
 
    *block_id = slot - e->slots;
 
1128
    *block_id = (uint32_t) (slot - e->slots);
1089
1129
 
1090
1130
    pa_mutex_unlock(e->mutex);
1091
1131
/*     pa_log("Got block id %u", *block_id); */
1105
1145
    pa_assert((uint8_t*) data + b->length <= (uint8_t*) memory->ptr + memory->size);
1106
1146
 
1107
1147
    *shm_id = memory->id;
1108
 
    *offset = (uint8_t*) data - (uint8_t*) memory->ptr;
 
1148
    *offset = (size_t) ((uint8_t*) data - (uint8_t*) memory->ptr);
1109
1149
    *size = b->length;
1110
1150
 
1111
1151
    pa_memblock_release(b);
1112
1152
 
1113
1153
    pa_atomic_inc(&e->pool->stat.n_exported);
1114
 
    pa_atomic_add(&e->pool->stat.exported_size, b->length);
 
1154
    pa_atomic_add(&e->pool->stat.exported_size, (int) b->length);
1115
1155
 
1116
1156
    return 0;
1117
1157
}