diff options
author | Michael Sevakis <jethead71@rockbox.org> | 2011-02-14 08:36:29 +0000 |
---|---|---|
committer | Michael Sevakis <jethead71@rockbox.org> | 2011-02-14 08:36:29 +0000 |
commit | 6938255b6b99f353652149057b5fe94fd0bb8f5d (patch) | |
tree | 49f2f613f7614654e90dd6b2c8508c1acced3d52 /apps | |
parent | 0fde635fb0c00641f372dfce14aff29c40e4398e (diff) | |
download | rockbox-6938255b6b99f353652149057b5fe94fd0bb8f5d.tar.gz rockbox-6938255b6b99f353652149057b5fe94fd0bb8f5d.zip |
Buffering: tin cup. Update threading structure and handle rebuffer more reliably on buffer thread using a single message send.
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@29303 a1c6a512-1295-4272-9138-f99709370657
Diffstat (limited to 'apps')
-rw-r--r-- | apps/buffering.c | 583 |
1 files changed, 319 insertions, 264 deletions
diff --git a/apps/buffering.c b/apps/buffering.c index f4eaf8a051..be6cf44aed 100644 --- a/apps/buffering.c +++ b/apps/buffering.c | |||
@@ -96,11 +96,9 @@ struct memory_handle { | |||
96 | enum data_type type; /* Type of data buffered with this handle */ | 96 | enum data_type type; /* Type of data buffered with this handle */ |
97 | char path[MAX_PATH]; /* Path if data originated in a file */ | 97 | char path[MAX_PATH]; /* Path if data originated in a file */ |
98 | int fd; /* File descriptor to path (-1 if closed) */ | 98 | int fd; /* File descriptor to path (-1 if closed) */ |
99 | size_t start; /* Start index of the handle's data buffer, | 99 | size_t data; /* Start index of the handle's data buffer */ |
100 | for use by reset_handle. */ | ||
101 | size_t data; /* Start index of the handle's data */ | ||
102 | volatile size_t ridx; /* Read pointer, relative to the main buffer */ | 100 | volatile size_t ridx; /* Read pointer, relative to the main buffer */ |
103 | size_t widx; /* Write pointer */ | 101 | size_t widx; /* Write pointer, relative to the main buffer */ |
104 | size_t filesize; /* File total length */ | 102 | size_t filesize; /* File total length */ |
105 | size_t filerem; /* Remaining bytes of file NOT in buffer */ | 103 | size_t filerem; /* Remaining bytes of file NOT in buffer */ |
106 | volatile size_t available; /* Available bytes to read from buffer */ | 104 | volatile size_t available; /* Available bytes to read from buffer */ |
@@ -109,6 +107,13 @@ struct memory_handle { | |||
109 | }; | 107 | }; |
110 | /* invariant: filesize == offset + available + filerem */ | 108 | /* invariant: filesize == offset + available + filerem */ |
111 | 109 | ||
110 | |||
111 | struct buf_message_data | ||
112 | { | ||
113 | int handle_id; | ||
114 | intptr_t data; | ||
115 | }; | ||
116 | |||
112 | static char *buffer; | 117 | static char *buffer; |
113 | static char *guard_buffer; | 118 | static char *guard_buffer; |
114 | 119 | ||
@@ -133,14 +138,14 @@ static int num_handles; /* number of handles in the list */ | |||
133 | 138 | ||
134 | static int base_handle_id; | 139 | static int base_handle_id; |
135 | 140 | ||
136 | static struct mutex llist_mutex; | 141 | /* Main lock for adding / removing handles */ |
137 | static struct mutex llist_mod_mutex; | 142 | static struct mutex llist_mutex SHAREDBSS_ATTR; |
138 | 143 | ||
139 | /* Handle cache (makes find_handle faster). | 144 | /* Handle cache (makes find_handle faster). |
140 | This is global so that move_handle and rm_handle can invalidate it. */ | 145 | This is global so that move_handle and rm_handle can invalidate it. */ |
141 | static struct memory_handle *cached_handle = NULL; | 146 | static struct memory_handle *cached_handle = NULL; |
142 | 147 | ||
143 | static struct { | 148 | static struct data_counters { |
144 | size_t remaining; /* Amount of data needing to be buffered */ | 149 | size_t remaining; /* Amount of data needing to be buffered */ |
145 | size_t wasted; /* Amount of space available for freeing */ | 150 | size_t wasted; /* Amount of space available for freeing */ |
146 | size_t buffered; /* Amount of data currently in the buffer */ | 151 | size_t buffered; /* Amount of data currently in the buffer */ |
@@ -152,8 +157,8 @@ static struct { | |||
152 | enum { | 157 | enum { |
153 | Q_BUFFER_HANDLE = 1, /* Request buffering of a handle, this should not be | 158 | Q_BUFFER_HANDLE = 1, /* Request buffering of a handle, this should not be |
154 | used in a low buffer situation. */ | 159 | used in a low buffer situation. */ |
155 | Q_RESET_HANDLE, /* (internal) Request resetting of a handle to its | 160 | Q_REBUFFER_HANDLE, /* Request reset and rebuffering of a handle at a new |
156 | offset (the offset has to be set beforehand) */ | 161 | file starting position. */ |
157 | Q_CLOSE_HANDLE, /* Request closing a handle */ | 162 | Q_CLOSE_HANDLE, /* Request closing a handle */ |
158 | Q_BASE_HANDLE, /* Set the reference handle for buf_useful_data */ | 163 | Q_BASE_HANDLE, /* Set the reference handle for buf_useful_data */ |
159 | 164 | ||
@@ -257,9 +262,6 @@ static struct memory_handle *add_handle(size_t data_size, bool can_wrap, | |||
257 | if (num_handles >= BUF_MAX_HANDLES) | 262 | if (num_handles >= BUF_MAX_HANDLES) |
258 | return NULL; | 263 | return NULL; |
259 | 264 | ||
260 | mutex_lock(&llist_mutex); | ||
261 | mutex_lock(&llist_mod_mutex); | ||
262 | |||
263 | widx = buf_widx; | 265 | widx = buf_widx; |
264 | 266 | ||
265 | if (cur_handle && cur_handle->filerem > 0) { | 267 | if (cur_handle && cur_handle->filerem > 0) { |
@@ -269,8 +271,6 @@ static struct memory_handle *add_handle(size_t data_size, bool can_wrap, | |||
269 | size_t req = cur_handle->filerem; | 271 | size_t req = cur_handle->filerem; |
270 | if (ringbuf_add_cross(cur_handle->widx, req, buf_ridx) >= 0) { | 272 | if (ringbuf_add_cross(cur_handle->widx, req, buf_ridx) >= 0) { |
271 | /* Not enough space to finish allocation */ | 273 | /* Not enough space to finish allocation */ |
272 | mutex_unlock(&llist_mod_mutex); | ||
273 | mutex_unlock(&llist_mutex); | ||
274 | return NULL; | 274 | return NULL; |
275 | } else { | 275 | } else { |
276 | /* Allocate the remainder of the space for the current handle */ | 276 | /* Allocate the remainder of the space for the current handle */ |
@@ -298,8 +298,6 @@ static struct memory_handle *add_handle(size_t data_size, bool can_wrap, | |||
298 | overlap = ringbuf_add_cross(widx, shift + len, buf_ridx); | 298 | overlap = ringbuf_add_cross(widx, shift + len, buf_ridx); |
299 | if (overlap >= 0 && (alloc_all || (size_t)overlap >= data_size)) { | 299 | if (overlap >= 0 && (alloc_all || (size_t)overlap >= data_size)) { |
300 | /* Not enough space for required allocations */ | 300 | /* Not enough space for required allocations */ |
301 | mutex_unlock(&llist_mod_mutex); | ||
302 | mutex_unlock(&llist_mutex); | ||
303 | return NULL; | 301 | return NULL; |
304 | } | 302 | } |
305 | 303 | ||
@@ -310,6 +308,9 @@ static struct memory_handle *add_handle(size_t data_size, bool can_wrap, | |||
310 | struct memory_handle *new_handle = | 308 | struct memory_handle *new_handle = |
311 | (struct memory_handle *)(&buffer[buf_widx]); | 309 | (struct memory_handle *)(&buffer[buf_widx]); |
312 | 310 | ||
311 | /* Prevent buffering thread from looking at it */ | ||
312 | new_handle->filerem = 0; | ||
313 | |||
313 | /* only advance the buffer write index of the size of the struct */ | 314 | /* only advance the buffer write index of the size of the struct */ |
314 | buf_widx = ringbuf_add(buf_widx, sizeof(struct memory_handle)); | 315 | buf_widx = ringbuf_add(buf_widx, sizeof(struct memory_handle)); |
315 | 316 | ||
@@ -328,8 +329,6 @@ static struct memory_handle *add_handle(size_t data_size, bool can_wrap, | |||
328 | 329 | ||
329 | cur_handle = new_handle; | 330 | cur_handle = new_handle; |
330 | 331 | ||
331 | mutex_unlock(&llist_mod_mutex); | ||
332 | mutex_unlock(&llist_mutex); | ||
333 | return new_handle; | 332 | return new_handle; |
334 | } | 333 | } |
335 | 334 | ||
@@ -340,9 +339,6 @@ static bool rm_handle(const struct memory_handle *h) | |||
340 | if (h == NULL) | 339 | if (h == NULL) |
341 | return true; | 340 | return true; |
342 | 341 | ||
343 | mutex_lock(&llist_mutex); | ||
344 | mutex_lock(&llist_mod_mutex); | ||
345 | |||
346 | if (h == first_handle) { | 342 | if (h == first_handle) { |
347 | first_handle = h->next; | 343 | first_handle = h->next; |
348 | if (h == cur_handle) { | 344 | if (h == cur_handle) { |
@@ -366,8 +362,6 @@ static bool rm_handle(const struct memory_handle *h) | |||
366 | buf_widx = cur_handle->widx; | 362 | buf_widx = cur_handle->widx; |
367 | } | 363 | } |
368 | } else { | 364 | } else { |
369 | mutex_unlock(&llist_mod_mutex); | ||
370 | mutex_unlock(&llist_mutex); | ||
371 | return false; | 365 | return false; |
372 | } | 366 | } |
373 | } | 367 | } |
@@ -377,9 +371,6 @@ static bool rm_handle(const struct memory_handle *h) | |||
377 | cached_handle = NULL; | 371 | cached_handle = NULL; |
378 | 372 | ||
379 | num_handles--; | 373 | num_handles--; |
380 | |||
381 | mutex_unlock(&llist_mod_mutex); | ||
382 | mutex_unlock(&llist_mutex); | ||
383 | return true; | 374 | return true; |
384 | } | 375 | } |
385 | 376 | ||
@@ -390,19 +381,15 @@ static struct memory_handle *find_handle(int handle_id) | |||
390 | if (handle_id < 0) | 381 | if (handle_id < 0) |
391 | return NULL; | 382 | return NULL; |
392 | 383 | ||
393 | mutex_lock(&llist_mutex); | ||
394 | |||
395 | /* simple caching because most of the time the requested handle | 384 | /* simple caching because most of the time the requested handle |
396 | will either be the same as the last, or the one after the last */ | 385 | will either be the same as the last, or the one after the last */ |
397 | if (cached_handle) | 386 | if (cached_handle) |
398 | { | 387 | { |
399 | if (cached_handle->id == handle_id) { | 388 | if (cached_handle->id == handle_id) { |
400 | mutex_unlock(&llist_mutex); | ||
401 | return cached_handle; | 389 | return cached_handle; |
402 | } else if (cached_handle->next && | 390 | } else if (cached_handle->next && |
403 | (cached_handle->next->id == handle_id)) { | 391 | (cached_handle->next->id == handle_id)) { |
404 | cached_handle = cached_handle->next; | 392 | cached_handle = cached_handle->next; |
405 | mutex_unlock(&llist_mutex); | ||
406 | return cached_handle; | 393 | return cached_handle; |
407 | } | 394 | } |
408 | } | 395 | } |
@@ -415,7 +402,6 @@ static struct memory_handle *find_handle(int handle_id) | |||
415 | if (m) | 402 | if (m) |
416 | cached_handle = m; | 403 | cached_handle = m; |
417 | 404 | ||
418 | mutex_unlock(&llist_mutex); | ||
419 | return m; | 405 | return m; |
420 | } | 406 | } |
421 | 407 | ||
@@ -449,9 +435,6 @@ static bool move_handle(struct memory_handle **h, size_t *delta, | |||
449 | return false; | 435 | return false; |
450 | } | 436 | } |
451 | 437 | ||
452 | mutex_lock(&llist_mutex); | ||
453 | mutex_lock(&llist_mod_mutex); | ||
454 | |||
455 | oldpos = ringbuf_offset(src); | 438 | oldpos = ringbuf_offset(src); |
456 | newpos = ringbuf_add(oldpos, final_delta); | 439 | newpos = ringbuf_add(oldpos, final_delta); |
457 | overlap = ringbuf_add_cross(newpos, size_to_move, buffer_len); | 440 | overlap = ringbuf_add_cross(newpos, size_to_move, buffer_len); |
@@ -477,8 +460,6 @@ static bool move_handle(struct memory_handle **h, size_t *delta, | |||
477 | correction = (correction + 3) & ~3; | 460 | correction = (correction + 3) & ~3; |
478 | if (final_delta < correction + sizeof(struct memory_handle)) { | 461 | if (final_delta < correction + sizeof(struct memory_handle)) { |
479 | /* Delta cannot end up less than the size of the struct */ | 462 | /* Delta cannot end up less than the size of the struct */ |
480 | mutex_unlock(&llist_mod_mutex); | ||
481 | mutex_unlock(&llist_mutex); | ||
482 | return false; | 463 | return false; |
483 | } | 464 | } |
484 | newpos -= correction; | 465 | newpos -= correction; |
@@ -500,8 +481,6 @@ static bool move_handle(struct memory_handle **h, size_t *delta, | |||
500 | if (m && m->next == src) { | 481 | if (m && m->next == src) { |
501 | m->next = dest; | 482 | m->next = dest; |
502 | } else { | 483 | } else { |
503 | mutex_unlock(&llist_mod_mutex); | ||
504 | mutex_unlock(&llist_mutex); | ||
505 | return false; | 484 | return false; |
506 | } | 485 | } |
507 | } | 486 | } |
@@ -562,8 +541,6 @@ static bool move_handle(struct memory_handle **h, size_t *delta, | |||
562 | /* Update the caller with the new location of h and the distance moved */ | 541 | /* Update the caller with the new location of h and the distance moved */ |
563 | *h = dest; | 542 | *h = dest; |
564 | *delta = final_delta; | 543 | *delta = final_delta; |
565 | mutex_unlock(&llist_mod_mutex); | ||
566 | mutex_unlock(&llist_mutex); | ||
567 | return true; | 544 | return true; |
568 | } | 545 | } |
569 | 546 | ||
@@ -575,7 +552,6 @@ BUFFER SPACE MANAGEMENT | |||
575 | update_data_counters: Updates the values in data_counters | 552 | update_data_counters: Updates the values in data_counters |
576 | buffer_is_low : Returns true if the amount of useful data in the buffer is low | 553 | buffer_is_low : Returns true if the amount of useful data in the buffer is low |
577 | buffer_handle : Buffer data for a handle | 554 | buffer_handle : Buffer data for a handle |
578 | reset_handle : Reset write position and data buffer of a handle to its offset | ||
579 | rebuffer_handle : Seek to a nonbuffered part of a handle by rebuffering the data | 555 | rebuffer_handle : Seek to a nonbuffered part of a handle by rebuffering the data |
580 | shrink_handle : Free buffer space by moving a handle | 556 | shrink_handle : Free buffer space by moving a handle |
581 | fill_buffer : Call buffer_handle for all handles that have data to buffer | 557 | fill_buffer : Call buffer_handle for all handles that have data to buffer |
@@ -583,18 +559,24 @@ fill_buffer : Call buffer_handle for all handles that have data to buffer | |||
583 | These functions are used by the buffering thread to manage buffer space. | 559 | These functions are used by the buffering thread to manage buffer space. |
584 | */ | 560 | */ |
585 | 561 | ||
586 | static void update_data_counters(void) | 562 | static void update_data_counters(struct data_counters *dc) |
587 | { | 563 | { |
588 | struct memory_handle *m = find_handle(base_handle_id); | ||
589 | bool is_useful = m==NULL; | ||
590 | |||
591 | size_t buffered = 0; | 564 | size_t buffered = 0; |
592 | size_t wasted = 0; | 565 | size_t wasted = 0; |
593 | size_t remaining = 0; | 566 | size_t remaining = 0; |
594 | size_t useful = 0; | 567 | size_t useful = 0; |
595 | 568 | ||
569 | struct memory_handle *m; | ||
570 | bool is_useful; | ||
571 | |||
572 | if (dc == NULL) | ||
573 | dc = &data_counters; | ||
574 | |||
596 | mutex_lock(&llist_mutex); | 575 | mutex_lock(&llist_mutex); |
597 | 576 | ||
577 | m = find_handle(base_handle_id); | ||
578 | is_useful = m == NULL; | ||
579 | |||
598 | m = first_handle; | 580 | m = first_handle; |
599 | while (m) { | 581 | while (m) { |
600 | buffered += m->available; | 582 | buffered += m->available; |
@@ -612,21 +594,21 @@ static void update_data_counters(void) | |||
612 | 594 | ||
613 | mutex_unlock(&llist_mutex); | 595 | mutex_unlock(&llist_mutex); |
614 | 596 | ||
615 | data_counters.buffered = buffered; | 597 | dc->buffered = buffered; |
616 | data_counters.wasted = wasted; | 598 | dc->wasted = wasted; |
617 | data_counters.remaining = remaining; | 599 | dc->remaining = remaining; |
618 | data_counters.useful = useful; | 600 | dc->useful = useful; |
619 | } | 601 | } |
620 | 602 | ||
621 | static inline bool buffer_is_low(void) | 603 | static inline bool buffer_is_low(void) |
622 | { | 604 | { |
623 | update_data_counters(); | 605 | update_data_counters(NULL); |
624 | return data_counters.useful < (conf_watermark / 2); | 606 | return data_counters.useful < (conf_watermark / 2); |
625 | } | 607 | } |
626 | 608 | ||
627 | /* Buffer data for the given handle. | 609 | /* Q_BUFFER_HANDLE event and buffer data for the given handle. |
628 | Return whether or not the buffering should continue explicitly. */ | 610 | Return whether or not the buffering should continue explicitly. */ |
629 | static bool buffer_handle(int handle_id) | 611 | static bool buffer_handle(int handle_id, size_t to_buffer) |
630 | { | 612 | { |
631 | logf("buffer_handle(%d)", handle_id); | 613 | logf("buffer_handle(%d)", handle_id); |
632 | struct memory_handle *h = find_handle(handle_id); | 614 | struct memory_handle *h = find_handle(handle_id); |
@@ -671,7 +653,7 @@ static bool buffer_handle(int handle_id) | |||
671 | h->filerem = 0; | 653 | h->filerem = 0; |
672 | h->available = sizeof(struct mp3entry); | 654 | h->available = sizeof(struct mp3entry); |
673 | h->widx += sizeof(struct mp3entry); | 655 | h->widx += sizeof(struct mp3entry); |
674 | send_event(BUFFER_EVENT_FINISHED, &h->id); | 656 | send_event(BUFFER_EVENT_FINISHED, &handle_id); |
675 | return true; | 657 | return true; |
676 | } | 658 | } |
677 | 659 | ||
@@ -730,113 +712,53 @@ static bool buffer_handle(int handle_id) | |||
730 | yield(); | 712 | yield(); |
731 | } | 713 | } |
732 | 714 | ||
733 | if (!queue_empty(&buffering_queue)) | 715 | if (to_buffer == 0) |
734 | break; | 716 | { |
717 | /* Normal buffering - check queue */ | ||
718 | if(!queue_empty(&buffering_queue)) | ||
719 | break; | ||
720 | } | ||
721 | else | ||
722 | { | ||
723 | if (to_buffer <= (size_t)rc) | ||
724 | break; /* Done */ | ||
725 | to_buffer -= rc; | ||
726 | } | ||
735 | } | 727 | } |
736 | 728 | ||
737 | if (h->filerem == 0) { | 729 | if (h->filerem == 0) { |
738 | /* finished buffering the file */ | 730 | /* finished buffering the file */ |
739 | close(h->fd); | 731 | close(h->fd); |
740 | h->fd = -1; | 732 | h->fd = -1; |
741 | send_event(BUFFER_EVENT_FINISHED, &h->id); | 733 | send_event(BUFFER_EVENT_FINISHED, &handle_id); |
742 | } | 734 | } |
743 | 735 | ||
744 | return !stop; | 736 | return !stop; |
745 | } | 737 | } |
746 | 738 | ||
747 | /* Reset writing position and data buffer of a handle to its current offset. | 739 | /* Close the specified handle id and free its allocation. */ |
748 | Use this after having set the new offset to use. */ | ||
749 | static void reset_handle(int handle_id) | ||
750 | { | ||
751 | size_t new_index; | ||
752 | |||
753 | logf("reset_handle(%d)", handle_id); | ||
754 | |||
755 | struct memory_handle *h = find_handle(handle_id); | ||
756 | if (!h) | ||
757 | return; | ||
758 | |||
759 | new_index = h->start; | ||
760 | |||
761 | #ifdef STORAGE_WANTS_ALIGN | ||
762 | /* Align to desired storage alignment if space permits - handle could have | ||
763 | been shrunken too close to the following one after a previous rebuffer. */ | ||
764 | size_t alignment_pad = STORAGE_OVERLAP(h->offset - (size_t)(&buffer[new_index])); | ||
765 | size_t offset = h->next ? ringbuf_offset(h->next) : buf_ridx; | ||
766 | |||
767 | if (ringbuf_add_cross(new_index, alignment_pad, offset) >= 0) { | ||
768 | /* Forego storage alignment this time */ | ||
769 | alignment_pad = 0; | ||
770 | } | ||
771 | |||
772 | new_index = ringbuf_add(new_index, alignment_pad); | ||
773 | #endif | ||
774 | |||
775 | h->ridx = h->widx = h->data = new_index; | ||
776 | |||
777 | if (h == cur_handle) | ||
778 | buf_widx = new_index; | ||
779 | |||
780 | h->available = 0; | ||
781 | h->filerem = h->filesize - h->offset; | ||
782 | |||
783 | if (h->fd >= 0) { | ||
784 | lseek(h->fd, h->offset, SEEK_SET); | ||
785 | } | ||
786 | } | ||
787 | |||
788 | /* Seek to a nonbuffered part of a handle by rebuffering the data. */ | ||
789 | static void rebuffer_handle(int handle_id, size_t newpos) | ||
790 | { | ||
791 | struct memory_handle *h = find_handle(handle_id); | ||
792 | if (!h) | ||
793 | return; | ||
794 | |||
795 | /* When seeking foward off of the buffer, if it is a short seek don't | ||
796 | rebuffer the whole track, just read enough to satisfy */ | ||
797 | if (newpos > h->offset && newpos - h->offset < BUFFERING_DEFAULT_FILECHUNK) | ||
798 | { | ||
799 | LOGFQUEUE("buffering >| Q_BUFFER_HANDLE %d", handle_id); | ||
800 | queue_send(&buffering_queue, Q_BUFFER_HANDLE, handle_id); | ||
801 | h->ridx = ringbuf_add(h->data, newpos - h->offset); | ||
802 | return; | ||
803 | } | ||
804 | |||
805 | h->offset = newpos; | ||
806 | |||
807 | /* Reset the handle to its new offset */ | ||
808 | LOGFQUEUE("buffering >| Q_RESET_HANDLE %d", handle_id); | ||
809 | queue_send(&buffering_queue, Q_RESET_HANDLE, handle_id); | ||
810 | |||
811 | uintptr_t next = ringbuf_offset(h->next); | ||
812 | if (ringbuf_sub(next, h->data) < h->filesize - newpos) | ||
813 | { | ||
814 | /* There isn't enough space to rebuffer all of the track from its new | ||
815 | offset, so we ask the user to free some */ | ||
816 | DEBUGF("%s(): space is needed\n", __func__); | ||
817 | send_event(BUFFER_EVENT_REBUFFER, &handle_id); | ||
818 | } | ||
819 | |||
820 | /* Now we ask for a rebuffer */ | ||
821 | LOGFQUEUE("buffering >| Q_BUFFER_HANDLE %d", handle_id); | ||
822 | queue_send(&buffering_queue, Q_BUFFER_HANDLE, handle_id); | ||
823 | } | ||
824 | |||
825 | static bool close_handle(int handle_id) | 740 | static bool close_handle(int handle_id) |
826 | { | 741 | { |
827 | struct memory_handle *h = find_handle(handle_id); | 742 | bool retval = true; |
743 | struct memory_handle *h; | ||
744 | |||
745 | mutex_lock(&llist_mutex); | ||
746 | h = find_handle(handle_id); | ||
828 | 747 | ||
829 | /* If the handle is not found, it is closed */ | 748 | /* If the handle is not found, it is closed */ |
830 | if (!h) | 749 | if (h) { |
831 | return true; | 750 | if (h->fd >= 0) { |
751 | close(h->fd); | ||
752 | h->fd = -1; | ||
753 | } | ||
832 | 754 | ||
833 | if (h->fd >= 0) { | 755 | /* rm_handle returns true unless the handle somehow persists after |
834 | close(h->fd); | 756 | exit */ |
835 | h->fd = -1; | 757 | retval = rm_handle(h); |
836 | } | 758 | } |
837 | 759 | ||
838 | /* rm_handle returns true unless the handle somehow persists after exit */ | 760 | mutex_unlock(&llist_mutex); |
839 | return rm_handle(h); | 761 | return retval; |
840 | } | 762 | } |
841 | 763 | ||
842 | /* Free buffer space by moving the handle struct right before the useful | 764 | /* Free buffer space by moving the handle struct right before the useful |
@@ -888,7 +810,6 @@ static void shrink_handle(struct memory_handle *h) | |||
888 | return; | 810 | return; |
889 | 811 | ||
890 | h->data = ringbuf_add(h->data, delta); | 812 | h->data = ringbuf_add(h->data, delta); |
891 | h->start = ringbuf_add(h->start, delta); | ||
892 | h->available -= delta; | 813 | h->available -= delta; |
893 | h->offset += delta; | 814 | h->offset += delta; |
894 | } | 815 | } |
@@ -900,12 +821,13 @@ static void shrink_handle(struct memory_handle *h) | |||
900 | static bool fill_buffer(void) | 821 | static bool fill_buffer(void) |
901 | { | 822 | { |
902 | logf("fill_buffer()"); | 823 | logf("fill_buffer()"); |
903 | struct memory_handle *m; | 824 | struct memory_handle *m = first_handle; |
904 | shrink_handle(first_handle); | 825 | |
905 | m = first_handle; | 826 | shrink_handle(m); |
827 | |||
906 | while (queue_empty(&buffering_queue) && m) { | 828 | while (queue_empty(&buffering_queue) && m) { |
907 | if (m->filerem > 0) { | 829 | if (m->filerem > 0) { |
908 | if (!buffer_handle(m->id)) { | 830 | if (!buffer_handle(m->id, 0)) { |
909 | m = NULL; | 831 | m = NULL; |
910 | break; | 832 | break; |
911 | } | 833 | } |
@@ -990,7 +912,7 @@ management functions for all the actual handle management work. | |||
990 | /* Reserve space in the buffer for a file. | 912 | /* Reserve space in the buffer for a file. |
991 | filename: name of the file to open | 913 | filename: name of the file to open |
992 | offset: offset at which to start buffering the file, useful when the first | 914 | offset: offset at which to start buffering the file, useful when the first |
993 | (offset-1) bytes of the file aren't needed. | 915 | offset bytes of the file aren't needed. |
994 | type: one of the data types supported (audio, image, cuesheet, others | 916 | type: one of the data types supported (audio, image, cuesheet, others |
995 | user_data: user data passed possibly passed in subcalls specific to a | 917 | user_data: user data passed possibly passed in subcalls specific to a |
996 | data_type (only used for image (albumart) buffering so far ) | 918 | data_type (only used for image (albumart) buffering so far ) |
@@ -1004,33 +926,40 @@ int bufopen(const char *file, size_t offset, enum data_type type, | |||
1004 | /* currently only used for aa loading */ | 926 | /* currently only used for aa loading */ |
1005 | (void)user_data; | 927 | (void)user_data; |
1006 | #endif | 928 | #endif |
929 | int handle_id = ERR_BUFFER_FULL; | ||
930 | |||
931 | /* No buffer refs until after the mutex_lock call! */ | ||
932 | |||
1007 | if (type == TYPE_ID3) | 933 | if (type == TYPE_ID3) |
1008 | { | 934 | { |
1009 | /* ID3 case: allocate space, init the handle and return. */ | 935 | /* ID3 case: allocate space, init the handle and return. */ |
936 | mutex_lock(&llist_mutex); | ||
1010 | 937 | ||
1011 | struct memory_handle *h = add_handle(sizeof(struct mp3entry), false, true); | 938 | struct memory_handle *h = add_handle(sizeof(struct mp3entry), false, true); |
1012 | if (!h) | 939 | if (h) |
1013 | return ERR_BUFFER_FULL; | 940 | { |
1014 | 941 | handle_id = h->id; | |
1015 | h->fd = -1; | 942 | h->fd = -1; |
1016 | h->filesize = sizeof(struct mp3entry); | 943 | h->filesize = sizeof(struct mp3entry); |
1017 | h->filerem = sizeof(struct mp3entry); | 944 | h->offset = 0; |
1018 | h->offset = 0; | 945 | h->data = buf_widx; |
1019 | h->data = buf_widx; | 946 | h->ridx = buf_widx; |
1020 | h->ridx = buf_widx; | 947 | h->widx = buf_widx; |
1021 | h->widx = buf_widx; | 948 | h->available = 0; |
1022 | h->available = 0; | 949 | h->type = type; |
1023 | h->type = type; | 950 | strlcpy(h->path, file, MAX_PATH); |
1024 | strlcpy(h->path, file, MAX_PATH); | 951 | |
1025 | 952 | buf_widx += sizeof(struct mp3entry); /* safe because the handle | |
1026 | buf_widx += sizeof(struct mp3entry); /* safe because the handle | 953 | can't wrap */ |
1027 | can't wrap */ | 954 | h->filerem = sizeof(struct mp3entry); |
1028 | 955 | ||
1029 | /* Inform the buffering thread that we added a handle */ | 956 | /* Inform the buffering thread that we added a handle */ |
1030 | LOGFQUEUE("buffering > Q_HANDLE_ADDED %d", h->id); | 957 | LOGFQUEUE("buffering > Q_HANDLE_ADDED %d", handle_id); |
1031 | queue_post(&buffering_queue, Q_HANDLE_ADDED, h->id); | 958 | queue_post(&buffering_queue, Q_HANDLE_ADDED, handle_id); |
959 | } | ||
1032 | 960 | ||
1033 | return h->id; | 961 | mutex_unlock(&llist_mutex); |
962 | return handle_id; | ||
1034 | } | 963 | } |
1035 | #ifdef APPLICATION | 964 | #ifdef APPLICATION |
1036 | /* loading code from memory is not supported in application builds */ | 965 | /* loading code from memory is not supported in application builds */ |
@@ -1062,37 +991,40 @@ int bufopen(const char *file, size_t offset, enum data_type type, | |||
1062 | 991 | ||
1063 | /* Reserve extra space because alignment can move data forward */ | 992 | /* Reserve extra space because alignment can move data forward */ |
1064 | size_t padded_size = STORAGE_PAD(size-adjusted_offset); | 993 | size_t padded_size = STORAGE_PAD(size-adjusted_offset); |
994 | |||
995 | mutex_lock(&llist_mutex); | ||
996 | |||
1065 | struct memory_handle *h = add_handle(padded_size, can_wrap, false); | 997 | struct memory_handle *h = add_handle(padded_size, can_wrap, false); |
1066 | if (!h) | 998 | if (!h) |
1067 | { | 999 | { |
1068 | DEBUGF("%s(): failed to add handle\n", __func__); | 1000 | DEBUGF("%s(): failed to add handle\n", __func__); |
1001 | mutex_unlock(&llist_mutex); | ||
1069 | close(fd); | 1002 | close(fd); |
1070 | return ERR_BUFFER_FULL; | 1003 | return ERR_BUFFER_FULL; |
1071 | } | 1004 | } |
1072 | 1005 | ||
1006 | handle_id = h->id; | ||
1073 | strlcpy(h->path, file, MAX_PATH); | 1007 | strlcpy(h->path, file, MAX_PATH); |
1074 | h->offset = adjusted_offset; | 1008 | h->offset = adjusted_offset; |
1075 | 1009 | ||
1010 | #ifdef STORAGE_WANTS_ALIGN | ||
1076 | /* Don't bother to storage align bitmaps because they are not | 1011 | /* Don't bother to storage align bitmaps because they are not |
1077 | * loaded directly into the buffer. | 1012 | * loaded directly into the buffer. |
1078 | */ | 1013 | */ |
1079 | if (type != TYPE_BITMAP) | 1014 | if (type != TYPE_BITMAP) |
1080 | { | 1015 | { |
1081 | size_t alignment_pad; | ||
1082 | |||
1083 | /* Remember where data area starts, for use by reset_handle */ | ||
1084 | h->start = buf_widx; | ||
1085 | |||
1086 | /* Align to desired storage alignment */ | 1016 | /* Align to desired storage alignment */ |
1087 | alignment_pad = STORAGE_OVERLAP(adjusted_offset - (size_t)(&buffer[buf_widx])); | 1017 | size_t alignment_pad = STORAGE_OVERLAP(adjusted_offset - |
1018 | (size_t)(&buffer[buf_widx])); | ||
1088 | buf_widx = ringbuf_add(buf_widx, alignment_pad); | 1019 | buf_widx = ringbuf_add(buf_widx, alignment_pad); |
1089 | } | 1020 | } |
1021 | #endif /* STORAGE_WANTS_ALIGN */ | ||
1090 | 1022 | ||
1023 | h->fd = -1; | ||
1024 | h->data = buf_widx; | ||
1091 | h->ridx = buf_widx; | 1025 | h->ridx = buf_widx; |
1092 | h->widx = buf_widx; | 1026 | h->widx = buf_widx; |
1093 | h->data = buf_widx; | ||
1094 | h->available = 0; | 1027 | h->available = 0; |
1095 | h->filerem = 0; | ||
1096 | h->type = type; | 1028 | h->type = type; |
1097 | 1029 | ||
1098 | #ifdef HAVE_ALBUMART | 1030 | #ifdef HAVE_ALBUMART |
@@ -1100,47 +1032,55 @@ int bufopen(const char *file, size_t offset, enum data_type type, | |||
1100 | { | 1032 | { |
1101 | /* Bitmap file: we load the data instead of the file */ | 1033 | /* Bitmap file: we load the data instead of the file */ |
1102 | int rc; | 1034 | int rc; |
1103 | mutex_lock(&llist_mod_mutex); /* Lock because load_bitmap yields */ | ||
1104 | rc = load_image(fd, file, (struct bufopen_bitmap_data*)user_data); | 1035 | rc = load_image(fd, file, (struct bufopen_bitmap_data*)user_data); |
1105 | mutex_unlock(&llist_mod_mutex); | ||
1106 | if (rc <= 0) | 1036 | if (rc <= 0) |
1107 | { | 1037 | { |
1108 | rm_handle(h); | 1038 | rm_handle(h); |
1109 | close(fd); | 1039 | handle_id = ERR_FILE_ERROR; |
1110 | return ERR_FILE_ERROR; | 1040 | } |
1041 | else | ||
1042 | { | ||
1043 | h->filesize = rc; | ||
1044 | h->available = rc; | ||
1045 | h->widx = buf_widx + rc; /* safe because the data doesn't wrap */ | ||
1046 | buf_widx += rc; /* safe too */ | ||
1111 | } | 1047 | } |
1112 | h->filerem = 0; | ||
1113 | h->filesize = rc; | ||
1114 | h->available = rc; | ||
1115 | h->widx = buf_widx + rc; /* safe because the data doesn't wrap */ | ||
1116 | buf_widx += rc; /* safe too */ | ||
1117 | } | 1048 | } |
1118 | else | 1049 | else |
1119 | #endif | 1050 | #endif |
1120 | { | 1051 | { |
1121 | h->filerem = size - adjusted_offset; | 1052 | if (type == TYPE_CUESHEET) |
1053 | h->fd = fd; | ||
1054 | |||
1122 | h->filesize = size; | 1055 | h->filesize = size; |
1123 | h->available = 0; | 1056 | h->available = 0; |
1124 | h->widx = buf_widx; | 1057 | h->widx = buf_widx; |
1058 | h->filerem = size - adjusted_offset; | ||
1125 | } | 1059 | } |
1126 | 1060 | ||
1127 | if (type == TYPE_CUESHEET) { | 1061 | mutex_unlock(&llist_mutex); |
1128 | h->fd = fd; | 1062 | |
1063 | if (type == TYPE_CUESHEET) | ||
1064 | { | ||
1129 | /* Immediately start buffering those */ | 1065 | /* Immediately start buffering those */ |
1130 | LOGFQUEUE("buffering >| Q_BUFFER_HANDLE %d", h->id); | 1066 | LOGFQUEUE("buffering >| Q_BUFFER_HANDLE %d", handle_id); |
1131 | queue_send(&buffering_queue, Q_BUFFER_HANDLE, h->id); | 1067 | queue_send(&buffering_queue, Q_BUFFER_HANDLE, handle_id); |
1132 | } else { | 1068 | } |
1069 | else | ||
1070 | { | ||
1133 | /* Other types will get buffered in the course of normal operations */ | 1071 | /* Other types will get buffered in the course of normal operations */ |
1134 | h->fd = -1; | ||
1135 | close(fd); | 1072 | close(fd); |
1136 | 1073 | ||
1137 | /* Inform the buffering thread that we added a handle */ | 1074 | if (handle_id >= 0) |
1138 | LOGFQUEUE("buffering > Q_HANDLE_ADDED %d", h->id); | 1075 | { |
1139 | queue_post(&buffering_queue, Q_HANDLE_ADDED, h->id); | 1076 | /* Inform the buffering thread that we added a handle */ |
1077 | LOGFQUEUE("buffering > Q_HANDLE_ADDED %d", handle_id); | ||
1078 | queue_post(&buffering_queue, Q_HANDLE_ADDED, handle_id); | ||
1079 | } | ||
1140 | } | 1080 | } |
1141 | 1081 | ||
1142 | logf("bufopen: new hdl %d", h->id); | 1082 | logf("bufopen: new hdl %d", handle_id); |
1143 | return h->id; | 1083 | return handle_id; |
1144 | } | 1084 | } |
1145 | 1085 | ||
1146 | /* Open a new handle from data that needs to be copied from memory. | 1086 | /* Open a new handle from data that needs to be copied from memory. |
@@ -1152,36 +1092,43 @@ int bufopen(const char *file, size_t offset, enum data_type type, | |||
1152 | */ | 1092 | */ |
1153 | int bufalloc(const void *src, size_t size, enum data_type type) | 1093 | int bufalloc(const void *src, size_t size, enum data_type type) |
1154 | { | 1094 | { |
1155 | struct memory_handle *h = add_handle(size, false, true); | 1095 | int handle_id = ERR_BUFFER_FULL; |
1156 | 1096 | ||
1157 | if (!h) | 1097 | mutex_lock(&llist_mutex); |
1158 | return ERR_BUFFER_FULL; | ||
1159 | 1098 | ||
1160 | if (src) { | 1099 | struct memory_handle *h = add_handle(size, false, true); |
1161 | if (type == TYPE_ID3 && size == sizeof(struct mp3entry)) { | 1100 | |
1162 | /* specially take care of struct mp3entry */ | 1101 | if (h) |
1163 | copy_mp3entry((struct mp3entry *)&buffer[buf_widx], | 1102 | { |
1164 | (const struct mp3entry *)src); | 1103 | handle_id = h->id; |
1165 | } else { | 1104 | |
1166 | memcpy(&buffer[buf_widx], src, size); | 1105 | if (src) { |
1106 | if (type == TYPE_ID3 && size == sizeof(struct mp3entry)) { | ||
1107 | /* specially take care of struct mp3entry */ | ||
1108 | copy_mp3entry((struct mp3entry *)&buffer[buf_widx], | ||
1109 | (const struct mp3entry *)src); | ||
1110 | } else { | ||
1111 | memcpy(&buffer[buf_widx], src, size); | ||
1112 | } | ||
1167 | } | 1113 | } |
1168 | } | 1114 | |
1115 | h->fd = -1; | ||
1116 | *h->path = 0; | ||
1117 | h->filesize = size; | ||
1118 | h->offset = 0; | ||
1119 | h->ridx = buf_widx; | ||
1120 | h->widx = buf_widx + size; /* this is safe because the data doesn't wrap */ | ||
1121 | h->data = buf_widx; | ||
1122 | h->available = size; | ||
1123 | h->type = type; | ||
1169 | 1124 | ||
1170 | h->fd = -1; | 1125 | buf_widx += size; /* safe too */ |
1171 | *h->path = 0; | 1126 | } |
1172 | h->filesize = size; | ||
1173 | h->filerem = 0; | ||
1174 | h->offset = 0; | ||
1175 | h->ridx = buf_widx; | ||
1176 | h->widx = buf_widx + size; /* this is safe because the data doesn't wrap */ | ||
1177 | h->data = buf_widx; | ||
1178 | h->available = size; | ||
1179 | h->type = type; | ||
1180 | 1127 | ||
1181 | buf_widx += size; /* safe too */ | 1128 | mutex_unlock(&llist_mutex); |
1182 | 1129 | ||
1183 | logf("bufalloc: new hdl %d", h->id); | 1130 | logf("bufalloc: new hdl %d", handle_id); |
1184 | return h->id; | 1131 | return handle_id; |
1185 | } | 1132 | } |
1186 | 1133 | ||
1187 | /* Close the handle. Return true for success and false for failure */ | 1134 | /* Close the handle. Return true for success and false for failure */ |
@@ -1193,56 +1140,155 @@ bool bufclose(int handle_id) | |||
1193 | return queue_send(&buffering_queue, Q_CLOSE_HANDLE, handle_id); | 1140 | return queue_send(&buffering_queue, Q_CLOSE_HANDLE, handle_id); |
1194 | } | 1141 | } |
1195 | 1142 | ||
1196 | /* Set reading index in handle (relatively to the start of the file). | 1143 | /* Backend to bufseek and bufadvance. Call only in response to |
1197 | Access before the available data will trigger a rebuffer. | 1144 | Q_REBUFFER_HANDLE! */ |
1198 | Return 0 for success and < 0 for failure: | 1145 | static void rebuffer_handle(int handle_id, size_t newpos) |
1199 | -1 if the handle wasn't found | ||
1200 | -2 if the new requested position was beyond the end of the file | ||
1201 | */ | ||
1202 | int bufseek(int handle_id, size_t newpos) | ||
1203 | { | 1146 | { |
1204 | struct memory_handle *h = find_handle(handle_id); | 1147 | struct memory_handle *h = find_handle(handle_id); |
1205 | if (!h) | 1148 | if (!h) |
1206 | return ERR_HANDLE_NOT_FOUND; | 1149 | { |
1150 | queue_reply(&buffering_queue, ERR_HANDLE_NOT_FOUND); | ||
1151 | return; | ||
1152 | } | ||
1153 | |||
1154 | /* When seeking foward off of the buffer, if it is a short seek attempt to | ||
1155 | avoid rebuffering the whole track, just read enough to satisfy */ | ||
1156 | if (newpos > h->offset && newpos - h->offset < BUFFERING_DEFAULT_FILECHUNK) | ||
1157 | { | ||
1158 | size_t amount = newpos - h->offset; | ||
1159 | h->ridx = ringbuf_add(h->data, amount); | ||
1160 | if (buffer_handle(handle_id, amount + 1)) | ||
1161 | { | ||
1162 | queue_reply(&buffering_queue, 0); | ||
1163 | buffer_handle(handle_id, 0); /* Ok, try the rest */ | ||
1164 | return; | ||
1165 | } | ||
1166 | /* Data collision - must reset */ | ||
1167 | } | ||
1168 | |||
1169 | /* Reset the handle to its new position */ | ||
1170 | h->offset = newpos; | ||
1171 | |||
1172 | size_t next = h->next ? ringbuf_offset(h->next) : buf_ridx; | ||
1173 | |||
1174 | #ifdef STORAGE_WANTS_ALIGN | ||
1175 | /* Strip alignment padding then redo */ | ||
1176 | size_t new_index = ringbuf_add(ringbuf_offset(h), sizeof (*h)); | ||
1177 | |||
1178 | /* Align to desired storage alignment if space permits - handle could have | ||
1179 | been shrunken too close to the following one after a previous rebuffer. */ | ||
1180 | size_t alignment_pad = | ||
1181 | STORAGE_OVERLAP(h->offset - (size_t)(&buffer[new_index])); | ||
1182 | |||
1183 | if (ringbuf_add_cross(new_index, alignment_pad, next) >= 0) | ||
1184 | alignment_pad = 0; /* Forego storage alignment this time */ | ||
1185 | |||
1186 | new_index = ringbuf_add(new_index, alignment_pad); | ||
1187 | #else | ||
1188 | /* Just clear the data buffer */ | ||
1189 | size_t new_index = h->data; | ||
1190 | #endif /* STORAGE_WANTS_ALIGN */ | ||
1191 | |||
1192 | h->ridx = h->widx = h->data = new_index; | ||
1193 | |||
1194 | if (h == cur_handle) | ||
1195 | buf_widx = new_index; | ||
1196 | |||
1197 | h->available = 0; | ||
1198 | h->filerem = h->filesize - h->offset; | ||
1199 | |||
1200 | if (h->fd >= 0) | ||
1201 | lseek(h->fd, h->offset, SEEK_SET); | ||
1202 | |||
1203 | if (h->next && ringbuf_sub(next, h->data) <= h->filesize - newpos) | ||
1204 | { | ||
1205 | /* There isn't enough space to rebuffer all of the track from its new | ||
1206 | offset, so we ask the user to free some */ | ||
1207 | DEBUGF("%s(): space is needed\n", __func__); | ||
1208 | int hid = handle_id; | ||
1209 | send_event(BUFFER_EVENT_REBUFFER, &hid); | ||
1210 | } | ||
1211 | |||
1212 | /* Now we do the rebuffer */ | ||
1213 | queue_reply(&buffering_queue, 0); | ||
1214 | buffer_handle(handle_id, 0); | ||
1215 | } | ||
1207 | 1216 | ||
1217 | /* Backend to bufseek and bufadvance */ | ||
1218 | static int seek_handle(struct memory_handle *h, size_t newpos) | ||
1219 | { | ||
1208 | if (newpos > h->filesize) { | 1220 | if (newpos > h->filesize) { |
1209 | /* access beyond the end of the file */ | 1221 | /* access beyond the end of the file */ |
1210 | return ERR_INVALID_VALUE; | 1222 | return ERR_INVALID_VALUE; |
1211 | } | 1223 | } |
1212 | else if (newpos < h->offset || h->offset + h->available < newpos) { | 1224 | else if ((newpos < h->offset || h->offset + h->available <= newpos) && |
1213 | /* access before or after buffered data. A rebuffer is needed. */ | 1225 | (newpos < h->filesize || h->filerem > 0)) { |
1214 | rebuffer_handle(handle_id, newpos); | 1226 | /* access before or after buffered data and not to end of file or file |
1227 | is not buffered to the end-- a rebuffer is needed. */ | ||
1228 | struct buf_message_data parm = { h->id, newpos }; | ||
1229 | return queue_send(&buffering_queue, Q_REBUFFER_HANDLE, | ||
1230 | (intptr_t)&parm); | ||
1215 | } | 1231 | } |
1216 | else { | 1232 | else { |
1217 | h->ridx = ringbuf_add(h->data, newpos - h->offset); | 1233 | h->ridx = ringbuf_add(h->data, newpos - h->offset); |
1218 | } | 1234 | } |
1235 | |||
1219 | return 0; | 1236 | return 0; |
1220 | } | 1237 | } |
1221 | 1238 | ||
1239 | /* Set reading index in handle (relatively to the start of the file). | ||
1240 | Access before the available data will trigger a rebuffer. | ||
1241 | Return 0 for success and < 0 for failure: | ||
1242 | -1 if the handle wasn't found | ||
1243 | -2 if the new requested position was beyond the end of the file | ||
1244 | */ | ||
1245 | int bufseek(int handle_id, size_t newpos) | ||
1246 | { | ||
1247 | struct memory_handle *h = find_handle(handle_id); | ||
1248 | if (!h) | ||
1249 | return ERR_HANDLE_NOT_FOUND; | ||
1250 | |||
1251 | return seek_handle(h, newpos); | ||
1252 | } | ||
1253 | |||
1222 | /* Advance the reading index in a handle (relatively to its current position). | 1254 | /* Advance the reading index in a handle (relatively to its current position). |
1223 | Return 0 for success and < 0 for failure */ | 1255 | Return 0 for success and < 0 for failure */ |
1224 | int bufadvance(int handle_id, off_t offset) | 1256 | int bufadvance(int handle_id, off_t offset) |
1225 | { | 1257 | { |
1226 | const struct memory_handle *h = find_handle(handle_id); | 1258 | struct memory_handle *h = find_handle(handle_id); |
1227 | if (!h) | 1259 | if (!h) |
1228 | return ERR_HANDLE_NOT_FOUND; | 1260 | return ERR_HANDLE_NOT_FOUND; |
1229 | 1261 | ||
1230 | size_t newpos = h->offset + ringbuf_sub(h->ridx, h->data) + offset; | 1262 | size_t newpos = h->offset + ringbuf_sub(h->ridx, h->data) + offset; |
1231 | return bufseek(handle_id, newpos); | 1263 | return seek_handle(h, newpos); |
1232 | } | 1264 | } |
1233 | 1265 | ||
1234 | /* Used by bufread and bufgetdata to prepare the buffer and retrieve the | 1266 | /* Used by bufread and bufgetdata to prepare the buffer and retrieve the |
1235 | * actual amount of data available for reading. This function explicitly | 1267 | * actual amount of data available for reading. This function explicitly |
1236 | * does not check the validity of the input handle. It does do range checks | 1268 | * does not check the validity of the input handle. It does do range checks |
1237 | * on size and returns a valid (and explicit) amount of data for reading */ | 1269 | * on size and returns a valid (and explicit) amount of data for reading */ |
1270 | static size_t handle_size_available(const struct memory_handle *h) | ||
1271 | { | ||
1272 | /* Obtain proper distances from data start */ | ||
1273 | size_t rd = ringbuf_sub(h->ridx, h->data); | ||
1274 | size_t wr = ringbuf_sub(h->widx, h->data); | ||
1275 | |||
1276 | if (LIKELY(wr > rd)) | ||
1277 | return wr - rd; | ||
1278 | |||
1279 | return 0; /* ridx is ahead of or equal to widx at this time */ | ||
1280 | } | ||
1281 | |||
1238 | static struct memory_handle *prep_bufdata(int handle_id, size_t *size, | 1282 | static struct memory_handle *prep_bufdata(int handle_id, size_t *size, |
1239 | bool guardbuf_limit) | 1283 | bool guardbuf_limit) |
1240 | { | 1284 | { |
1241 | struct memory_handle *h = find_handle(handle_id); | 1285 | struct memory_handle *h = find_handle(handle_id); |
1286 | size_t realsize; | ||
1287 | |||
1242 | if (!h) | 1288 | if (!h) |
1243 | return NULL; | 1289 | return NULL; |
1244 | 1290 | ||
1245 | size_t avail = ringbuf_sub(h->widx, h->ridx); | 1291 | size_t avail = handle_size_available(h); |
1246 | 1292 | ||
1247 | if (avail == 0 && h->filerem == 0) | 1293 | if (avail == 0 && h->filerem == 0) |
1248 | { | 1294 | { |
@@ -1251,40 +1297,52 @@ static struct memory_handle *prep_bufdata(int handle_id, size_t *size, | |||
1251 | return h; | 1297 | return h; |
1252 | } | 1298 | } |
1253 | 1299 | ||
1254 | if (*size == 0 || *size > avail + h->filerem) | 1300 | realsize = *size; |
1255 | *size = avail + h->filerem; | 1301 | |
1302 | if (realsize == 0 || realsize > avail + h->filerem) | ||
1303 | realsize = avail + h->filerem; | ||
1256 | 1304 | ||
1257 | if (guardbuf_limit && h->type == TYPE_PACKET_AUDIO && *size > GUARD_BUFSIZE) | 1305 | if (guardbuf_limit && h->type == TYPE_PACKET_AUDIO |
1306 | && realsize > GUARD_BUFSIZE) | ||
1258 | { | 1307 | { |
1259 | logf("data request > guardbuf"); | 1308 | logf("data request > guardbuf"); |
1260 | /* If more than the size of the guardbuf is requested and this is a | 1309 | /* If more than the size of the guardbuf is requested and this is a |
1261 | * bufgetdata, limit to guard_bufsize over the end of the buffer */ | 1310 | * bufgetdata, limit to guard_bufsize over the end of the buffer */ |
1262 | *size = MIN(*size, buffer_len - h->ridx + GUARD_BUFSIZE); | 1311 | realsize = MIN(realsize, buffer_len - h->ridx + GUARD_BUFSIZE); |
1263 | /* this ensures *size <= buffer_len - h->ridx + GUARD_BUFSIZE */ | 1312 | /* this ensures *size <= buffer_len - h->ridx + GUARD_BUFSIZE */ |
1264 | } | 1313 | } |
1265 | 1314 | ||
1266 | if (h->filerem > 0 && avail < *size) | 1315 | if (h->filerem > 0 && avail < realsize) |
1267 | { | 1316 | { |
1268 | /* Data isn't ready. Request buffering */ | 1317 | /* Data isn't ready. Request buffering */ |
1269 | buf_request_buffer_handle(handle_id); | 1318 | buf_request_buffer_handle(handle_id); |
1270 | /* Wait for the data to be ready */ | 1319 | /* Wait for the data to be ready */ |
1271 | do | 1320 | do |
1272 | { | 1321 | { |
1273 | sleep(1); | 1322 | sleep(0); |
1274 | /* it is not safe for a non-buffering thread to sleep while | 1323 | /* it is not safe for a non-buffering thread to sleep while |
1275 | * holding a handle */ | 1324 | * holding a handle */ |
1276 | h = find_handle(handle_id); | 1325 | h = find_handle(handle_id); |
1277 | if (!h) | 1326 | if (!h) |
1278 | return NULL; | 1327 | return NULL; |
1279 | avail = ringbuf_sub(h->widx, h->ridx); | 1328 | avail = handle_size_available(h); |
1280 | } | 1329 | } |
1281 | while (h->filerem > 0 && avail < *size); | 1330 | while (h->filerem > 0 && avail < realsize); |
1282 | } | 1331 | } |
1283 | 1332 | ||
1284 | *size = MIN(*size,avail); | 1333 | *size = MIN(realsize, avail); |
1285 | return h; | 1334 | return h; |
1286 | } | 1335 | } |
1287 | 1336 | ||
1337 | |||
1338 | /* Note: It is safe for the thread responsible for handling the rebuffer | ||
1339 | * cleanup request to call bufread or bufgetdata only when the data will | ||
1340 | * be available-- not if it could be blocked waiting for it in prep_bufdata. | ||
1341 | * It should be apparent that if said thread is being forced to wait for | ||
1342 | * buffering but has not yet responded to the cleanup request, the space | ||
1343 | * can never be cleared to allow further reading of the file because it is | ||
1344 | * not listening to callbacks any longer. */ | ||
1345 | |||
1288 | /* Copy data from the given handle to the dest buffer. | 1346 | /* Copy data from the given handle to the dest buffer. |
1289 | Return the number of bytes copied or < 0 for failure (handle not found). | 1347 | Return the number of bytes copied or < 0 for failure (handle not found). |
1290 | The caller is blocked until the requested amount of data is available. | 1348 | The caller is blocked until the requested amount of data is available. |
@@ -1481,6 +1539,7 @@ void buffering_thread(void) | |||
1481 | { | 1539 | { |
1482 | bool filling = false; | 1540 | bool filling = false; |
1483 | struct queue_event ev; | 1541 | struct queue_event ev; |
1542 | struct buf_message_data *parm; | ||
1484 | 1543 | ||
1485 | while (true) | 1544 | while (true) |
1486 | { | 1545 | { |
@@ -1499,19 +1558,20 @@ void buffering_thread(void) | |||
1499 | send_event(BUFFER_EVENT_BUFFER_LOW, 0); | 1558 | send_event(BUFFER_EVENT_BUFFER_LOW, 0); |
1500 | shrink_buffer(); | 1559 | shrink_buffer(); |
1501 | queue_reply(&buffering_queue, 1); | 1560 | queue_reply(&buffering_queue, 1); |
1502 | filling |= buffer_handle((int)ev.data); | 1561 | filling |= buffer_handle((int)ev.data, 0); |
1503 | break; | 1562 | break; |
1504 | 1563 | ||
1505 | case Q_BUFFER_HANDLE: | 1564 | case Q_BUFFER_HANDLE: |
1506 | LOGFQUEUE("buffering < Q_BUFFER_HANDLE %d", (int)ev.data); | 1565 | LOGFQUEUE("buffering < Q_BUFFER_HANDLE %d", (int)ev.data); |
1507 | queue_reply(&buffering_queue, 1); | 1566 | queue_reply(&buffering_queue, 1); |
1508 | buffer_handle((int)ev.data); | 1567 | buffer_handle((int)ev.data, 0); |
1509 | break; | 1568 | break; |
1510 | 1569 | ||
1511 | case Q_RESET_HANDLE: | 1570 | case Q_REBUFFER_HANDLE: |
1512 | LOGFQUEUE("buffering < Q_RESET_HANDLE %d", (int)ev.data); | 1571 | parm = (struct buf_message_data *)ev.data; |
1513 | queue_reply(&buffering_queue, 1); | 1572 | LOGFQUEUE("buffering < Q_REBUFFER_HANDLE %d %ld", |
1514 | reset_handle((int)ev.data); | 1573 | parm->handle_id, parm->data); |
1574 | rebuffer_handle(parm->handle_id, parm->data); | ||
1515 | break; | 1575 | break; |
1516 | 1576 | ||
1517 | case Q_CLOSE_HANDLE: | 1577 | case Q_CLOSE_HANDLE: |
@@ -1543,7 +1603,7 @@ void buffering_thread(void) | |||
1543 | break; | 1603 | break; |
1544 | } | 1604 | } |
1545 | 1605 | ||
1546 | update_data_counters(); | 1606 | update_data_counters(NULL); |
1547 | 1607 | ||
1548 | /* If the buffer is low, call the callbacks to get new data */ | 1608 | /* If the buffer is low, call the callbacks to get new data */ |
1549 | if (num_handles > 0 && data_counters.useful <= conf_watermark) | 1609 | if (num_handles > 0 && data_counters.useful <= conf_watermark) |
@@ -1565,7 +1625,7 @@ void buffering_thread(void) | |||
1565 | if (!filling) | 1625 | if (!filling) |
1566 | shrink_buffer(); | 1626 | shrink_buffer(); |
1567 | filling = fill_buffer(); | 1627 | filling = fill_buffer(); |
1568 | update_data_counters(); | 1628 | update_data_counters(NULL); |
1569 | } | 1629 | } |
1570 | } | 1630 | } |
1571 | #endif | 1631 | #endif |
@@ -1593,12 +1653,6 @@ void buffering_thread(void) | |||
1593 | void buffering_init(void) | 1653 | void buffering_init(void) |
1594 | { | 1654 | { |
1595 | mutex_init(&llist_mutex); | 1655 | mutex_init(&llist_mutex); |
1596 | mutex_init(&llist_mod_mutex); | ||
1597 | #ifdef HAVE_PRIORITY_SCHEDULING | ||
1598 | /* This behavior not safe atm */ | ||
1599 | mutex_set_preempt(&llist_mutex, false); | ||
1600 | mutex_set_preempt(&llist_mod_mutex, false); | ||
1601 | #endif | ||
1602 | 1656 | ||
1603 | conf_watermark = BUFFERING_DEFAULT_WATERMARK; | 1657 | conf_watermark = BUFFERING_DEFAULT_WATERMARK; |
1604 | 1658 | ||
@@ -1648,11 +1702,12 @@ bool buffering_reset(char *buf, size_t buflen) | |||
1648 | 1702 | ||
1649 | void buffering_get_debugdata(struct buffering_debug *dbgdata) | 1703 | void buffering_get_debugdata(struct buffering_debug *dbgdata) |
1650 | { | 1704 | { |
1651 | update_data_counters(); | 1705 | struct data_counters dc; |
1706 | update_data_counters(&dc); | ||
1652 | dbgdata->num_handles = num_handles; | 1707 | dbgdata->num_handles = num_handles; |
1653 | dbgdata->data_rem = data_counters.remaining; | 1708 | dbgdata->data_rem = dc.remaining; |
1654 | dbgdata->wasted_space = data_counters.wasted; | 1709 | dbgdata->wasted_space = dc.wasted; |
1655 | dbgdata->buffered_data = data_counters.buffered; | 1710 | dbgdata->buffered_data = dc.buffered; |
1656 | dbgdata->useful_data = data_counters.useful; | 1711 | dbgdata->useful_data = dc.useful; |
1657 | dbgdata->watermark = conf_watermark; | 1712 | dbgdata->watermark = conf_watermark; |
1658 | } | 1713 | } |