1/*
2 Copyright (c) 2000, 2011, Oracle and/or its affiliates
3 Copyright (c) 2010, 2015, MariaDB
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; version 2 of the License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
17
18/*
19 Cashing of files with only does (sequential) read or writes of fixed-
20 length records. A read isn't allowed to go over file-length. A read is ok
21 if it ends at file-length and next read can try to read after file-length
22 (and get a EOF-error).
23 Possibly use of asyncronic io.
24 macros for read and writes for faster io.
25 Used instead of FILE when reading or writing whole files.
26 One can change info->pos_in_file to a higher value to skip bytes in file if
27 also info->read_pos is set to info->read_end.
28 If called through open_cached_file(), then the temporary file will
29 only be created if a write exeeds the file buffer or if one calls
30 my_b_flush_io_cache().
31
32 If one uses SEQ_READ_APPEND, then two buffers are allocated, one for
33 reading and another for writing. Reads are first done from disk and
34 then done from the write buffer. This is an efficient way to read
35 from a log file when one is writing to it at the same time.
36 For this to work, the file has to be opened in append mode!
37 Note that when one uses SEQ_READ_APPEND, one MUST write using
38 my_b_append ! This is needed because we need to lock the mutex
39 every time we access the write buffer.
40
41TODO:
42 When one SEQ_READ_APPEND and we are reading and writing at the same time,
43 each time the write buffer gets full and it's written to disk, we will
44 always do a disk read to read a part of the buffer from disk to the
45 read buffer.
46 This should be fixed so that when we do a my_b_flush_io_cache() and
47 we have been reading the write buffer, we should transfer the rest of the
48 write buffer to the read buffer before we start to reuse it.
49*/
50
51#include "mysys_priv.h"
52#include <m_string.h>
53#ifdef HAVE_AIOWAIT
54#include "mysys_err.h"
55static void my_aiowait(my_aio_result *result);
56#endif
57#include <errno.h>
58
59#define lock_append_buffer(info) \
60 mysql_mutex_lock(&(info)->append_buffer_lock)
61#define unlock_append_buffer(info) \
62 mysql_mutex_unlock(&(info)->append_buffer_lock)
63
64#define IO_ROUND_UP(X) (((X)+IO_SIZE-1) & ~(IO_SIZE-1))
65#define IO_ROUND_DN(X) ( (X) & ~(IO_SIZE-1))
66
67static int _my_b_cache_read(IO_CACHE *info, uchar *Buffer, size_t Count);
68static int _my_b_cache_read_r(IO_CACHE *info, uchar *Buffer, size_t Count);
69static int _my_b_seq_read(IO_CACHE *info, uchar *Buffer, size_t Count);
70static int _my_b_cache_write(IO_CACHE *info, const uchar *Buffer, size_t Count);
71static int _my_b_cache_write_r(IO_CACHE *info, const uchar *Buffer, size_t Count);
72
73int (*_my_b_encr_read)(IO_CACHE *info,uchar *Buffer,size_t Count)= 0;
74int (*_my_b_encr_write)(IO_CACHE *info,const uchar *Buffer,size_t Count)= 0;
75
76
77
78static void
79init_functions(IO_CACHE* info)
80{
81 enum cache_type type= info->type;
82 info->read_function = 0; /* Force a core if used */
83 info->write_function = 0; /* Force a core if used */
84 switch (type) {
85 case READ_NET:
86 /*
87 Must be initialized by the caller. The problem is that
88 _my_b_net_read has to be defined in sql directory because of
89 the dependency on THD, and therefore cannot be visible to
90 programs that link against mysys but know nothing about THD, such
91 as myisamchk
92 */
93 DBUG_ASSERT(!(info->myflags & MY_ENCRYPT));
94 break;
95 case SEQ_READ_APPEND:
96 info->read_function = _my_b_seq_read;
97 DBUG_ASSERT(!(info->myflags & MY_ENCRYPT));
98 break;
99 case READ_CACHE:
100 if (info->myflags & MY_ENCRYPT)
101 {
102 DBUG_ASSERT(info->share == 0);
103 info->read_function = _my_b_encr_read;
104 break;
105 }
106 /* fall through */
107 case WRITE_CACHE:
108 if (info->myflags & MY_ENCRYPT)
109 {
110 info->write_function = _my_b_encr_write;
111 break;
112 }
113 /* fall through */
114 case READ_FIFO:
115 DBUG_ASSERT(!(info->myflags & MY_ENCRYPT));
116 info->read_function = info->share ? _my_b_cache_read_r : _my_b_cache_read;
117 info->write_function = info->share ? _my_b_cache_write_r : _my_b_cache_write;
118 info->myflags&= ~MY_FULL_IO;
119 break;
120 case TYPE_NOT_SET:
121 DBUG_ASSERT(0);
122 break;
123 }
124}
125
126
127/*
128 Initialize an IO_CACHE object
129
130 SYNOPSOS
131 init_io_cache()
132 info cache handler to initialize
133 file File that should be associated to to the handler
134 If == -1 then real_open_cached_file()
135 will be called when it's time to open file.
136 cachesize Size of buffer to allocate for read/write
137 If == 0 then use my_default_record_cache_size
138 type Type of cache
139 seek_offset Where cache should start reading/writing
140 use_async_io Set to 1 of we should use async_io (if available)
141 cache_myflags Bitmap of different flags
142 MY_WME | MY_FAE | MY_NABP | MY_FNABP |
143 MY_DONT_CHECK_FILESIZE
144
145 RETURN
146 0 ok
147 # error
148*/
149
150int init_io_cache(IO_CACHE *info, File file, size_t cachesize,
151 enum cache_type type, my_off_t seek_offset,
152 my_bool use_async_io, myf cache_myflags)
153{
154 size_t min_cache;
155 my_off_t pos;
156 my_off_t end_of_file= ~(my_off_t) 0;
157 DBUG_ENTER("init_io_cache");
158 DBUG_PRINT("enter",("cache:%p type: %d pos: %llu",
159 info, (int) type, (ulonglong) seek_offset));
160
161 info->file= file;
162 info->type= TYPE_NOT_SET; /* Don't set it until mutex are created */
163 info->pos_in_file= seek_offset;
164 info->alloced_buffer = 0;
165 info->buffer=0;
166 info->seek_not_done= 0;
167 info->next_file_user= NULL;
168
169 if (file >= 0)
170 {
171 DBUG_ASSERT(!(cache_myflags & MY_ENCRYPT));
172 pos= mysql_file_tell(file, MYF(0));
173 if ((pos == (my_off_t) -1) && (my_errno == ESPIPE))
174 {
175 /*
176 This kind of object doesn't support seek() or tell(). Don't set a
177 seek_not_done that will make us again try to seek() later and fail.
178
179 Additionally, if we're supposed to start somewhere other than the
180 the beginning of whatever this file is, then somebody made a bad
181 assumption.
182 */
183 DBUG_ASSERT(seek_offset == 0);
184 }
185 else
186 info->seek_not_done= MY_TEST(seek_offset != pos);
187 }
188 else
189 if (type == WRITE_CACHE && _my_b_encr_read)
190 {
191 cache_myflags|= MY_ENCRYPT;
192 DBUG_ASSERT(seek_offset == 0);
193 }
194
195 info->disk_writes= 0;
196 info->share=0;
197
198 if (!cachesize && !(cachesize= my_default_record_cache_size))
199 DBUG_RETURN(1); /* No cache requested */
200 min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2;
201 if (type == READ_CACHE || type == SEQ_READ_APPEND)
202 { /* Assume file isn't growing */
203 DBUG_ASSERT(!(cache_myflags & MY_ENCRYPT));
204 if (!(cache_myflags & MY_DONT_CHECK_FILESIZE))
205 {
206 /* Calculate end of file to avoid allocating oversized buffers */
207 end_of_file= mysql_file_seek(file, 0L, MY_SEEK_END, MYF(0));
208 /* Need to reset seek_not_done now that we just did a seek. */
209 info->seek_not_done= end_of_file == seek_offset ? 0 : 1;
210 if (end_of_file < seek_offset)
211 end_of_file=seek_offset;
212 /* Trim cache size if the file is very small */
213 if ((my_off_t) cachesize > end_of_file-seek_offset+IO_SIZE*2-1)
214 {
215 cachesize= (size_t) (end_of_file-seek_offset)+IO_SIZE*2-1;
216 use_async_io=0; /* No need to use async */
217 }
218 }
219 }
220 cache_myflags &= ~MY_DONT_CHECK_FILESIZE;
221 if (type != READ_NET)
222 {
223 /* Retry allocating memory in smaller blocks until we get one */
224 cachesize= ((cachesize + min_cache-1) & ~(min_cache-1));
225 for (;;)
226 {
227 size_t buffer_block;
228 /*
229 Unset MY_WAIT_IF_FULL bit if it is set, to prevent conflict with
230 MY_ZEROFILL.
231 */
232 myf flags= (myf) (cache_myflags & ~(MY_WME | MY_WAIT_IF_FULL));
233
234 if (cachesize < min_cache)
235 cachesize = min_cache;
236 buffer_block= cachesize;
237 if (type == SEQ_READ_APPEND)
238 buffer_block *= 2;
239 else if (cache_myflags & MY_ENCRYPT)
240 buffer_block= 2*(buffer_block + MY_AES_BLOCK_SIZE) + sizeof(IO_CACHE_CRYPT);
241 if (cachesize == min_cache)
242 flags|= (myf) MY_WME;
243
244 if ((info->buffer= (uchar*) my_malloc(buffer_block, flags)) != 0)
245 {
246 if (type == SEQ_READ_APPEND)
247 info->write_buffer= info->buffer + cachesize;
248 else
249 info->write_buffer= info->buffer;
250 info->alloced_buffer= 1;
251 break; /* Enough memory found */
252 }
253 if (cachesize == min_cache)
254 DBUG_RETURN(2); /* Can't alloc cache */
255 /* Try with less memory */
256 cachesize= (cachesize*3/4 & ~(min_cache-1));
257 }
258 }
259
260 DBUG_PRINT("info",("init_io_cache: cachesize = %lu", (ulong) cachesize));
261 info->read_length=info->buffer_length=cachesize;
262 info->myflags=cache_myflags & ~(MY_NABP | MY_FNABP);
263 info->request_pos= info->read_pos= info->write_pos = info->buffer;
264 if (type == SEQ_READ_APPEND)
265 {
266 info->append_read_pos = info->write_pos = info->write_buffer;
267 info->write_end = info->write_buffer + info->buffer_length;
268 mysql_mutex_init(key_IO_CACHE_append_buffer_lock,
269 &info->append_buffer_lock, MY_MUTEX_INIT_FAST);
270 }
271#if defined(SAFE_MUTEX)
272 else
273 {
274 /* Clear mutex so that safe_mutex will notice that it's not initialized */
275 bzero((char*) &info->append_buffer_lock, sizeof(info->append_buffer_lock));
276 }
277#endif
278
279 if (type == WRITE_CACHE)
280 info->write_end=
281 info->buffer+info->buffer_length- (seek_offset & (IO_SIZE-1));
282 else
283 info->read_end=info->buffer; /* Nothing in cache */
284
285 /* End_of_file may be changed by user later */
286 info->end_of_file= end_of_file;
287 info->error=0;
288 info->type= type;
289 init_functions(info);
290#ifdef HAVE_AIOWAIT
291 if (use_async_io && ! my_disable_async_io)
292 {
293 DBUG_PRINT("info",("Using async io"));
294 DBUG_ASSERT(!(cache_myflags & MY_ENCRYPT));
295 info->read_length/=2;
296 info->read_function=_my_b_async_read;
297 }
298 info->inited=info->aio_result.pending=0;
299#endif
300 DBUG_RETURN(0);
301} /* init_io_cache */
302
303
304
305/*
306 Initialize the slave IO_CACHE to read the same file (and data)
307 as master does.
308
309 One can create multiple slaves from a single master. Every slave and master
310 will have independent file positions.
311
312 The master must be a non-shared READ_CACHE.
313 It is assumed that no more reads are done after a master and/or a slave
314 has been freed (this limitation can be easily lifted).
315*/
316
317int init_slave_io_cache(IO_CACHE *master, IO_CACHE *slave)
318{
319 uchar *slave_buf;
320 DBUG_ASSERT(master->type == READ_CACHE);
321 DBUG_ASSERT(!master->share);
322 DBUG_ASSERT(master->alloced_buffer);
323
324 if (!(slave_buf= (uchar*)my_malloc(master->buffer_length, MYF(0))))
325 {
326 return 1;
327 }
328 memcpy(slave, master, sizeof(IO_CACHE));
329 slave->buffer= slave_buf;
330
331 memcpy(slave->buffer, master->buffer, master->buffer_length);
332 slave->read_pos= slave->buffer + (master->read_pos - master->buffer);
333 slave->read_end= slave->buffer + (master->read_end - master->buffer);
334
335 if (master->next_file_user)
336 {
337 IO_CACHE *p;
338 for (p= master->next_file_user;
339 p->next_file_user !=master;
340 p= p->next_file_user)
341 {}
342
343 p->next_file_user= slave;
344 slave->next_file_user= master;
345 }
346 else
347 {
348 slave->next_file_user= master;
349 master->next_file_user= slave;
350 }
351 return 0;
352}
353
354
355void end_slave_io_cache(IO_CACHE *cache)
356{
357 my_free(cache->buffer);
358}
359
360/*
361 Seek a read io cache to a given offset
362*/
363void seek_io_cache(IO_CACHE *cache, my_off_t needed_offset)
364{
365 my_off_t cached_data_start= cache->pos_in_file;
366 my_off_t cached_data_end= cache->pos_in_file + (cache->read_end -
367 cache->buffer);
368
369 if (needed_offset >= cached_data_start &&
370 needed_offset < cached_data_end)
371 {
372 /*
373 The offset we're seeking to is in the buffer.
374 Move buffer's read position accordingly
375 */
376 cache->read_pos= cache->buffer + (needed_offset - cached_data_start);
377 }
378 else
379 {
380 if (needed_offset > cache->end_of_file)
381 needed_offset= cache->end_of_file;
382 /*
383 The offset we're seeking to is not in the buffer.
384 - Set the buffer to be exhausted.
385 - Make the next read to a mysql_file_seek() call to the required
386 offset.
387 TODO(cvicentiu, spetrunia) properly implement aligned seeks for
388 efficiency.
389 */
390 cache->seek_not_done= 1;
391 cache->pos_in_file= needed_offset;
392 /* When reading it must appear as if we've started from the offset
393 that we've seeked here. We must let _my_b_cache_read assume that
394 by implying "no reading starting from pos_in_file" has happened. */
395 cache->read_pos= cache->buffer;
396 cache->read_end= cache->buffer;
397 }
398}
399
400 /* Wait until current request is ready */
401
402#ifdef HAVE_AIOWAIT
403static void my_aiowait(my_aio_result *result)
404{
405 if (result->pending)
406 {
407 struct aio_result_t *tmp;
408 for (;;)
409 {
410 if ((int) (tmp=aiowait((struct timeval *) 0)) == -1)
411 {
412 if (errno == EINTR)
413 continue;
414 DBUG_PRINT("error",("No aio request, error: %d",errno));
415 result->pending=0; /* Assume everything is ok */
416 break;
417 }
418 ((my_aio_result*) tmp)->pending=0;
419 if ((my_aio_result*) tmp == result)
420 break;
421 }
422 }
423 return;
424}
425#endif
426
427
428/*
429 Use this to reset cache to re-start reading or to change the type
430 between READ_CACHE <-> WRITE_CACHE
431 If we are doing a reinit of a cache where we have the start of the file
432 in the cache, we are reusing this memory without flushing it to disk.
433*/
434
435my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type,
436 my_off_t seek_offset,
437 my_bool use_async_io __attribute__((unused)),
438 my_bool clear_cache)
439{
440 DBUG_ENTER("reinit_io_cache");
441 DBUG_PRINT("enter",("cache:%p type: %d seek_offset: %llu clear_cache: %d",
442 info, type, (ulonglong) seek_offset,
443 (int) clear_cache));
444
445 DBUG_ASSERT(type == READ_CACHE || type == WRITE_CACHE);
446 DBUG_ASSERT(info->type == READ_CACHE || info->type == WRITE_CACHE);
447
448 /* If the whole file is in memory, avoid flushing to disk */
449 if (! clear_cache &&
450 seek_offset >= info->pos_in_file &&
451 seek_offset <= my_b_tell(info))
452 {
453 /* Reuse current buffer without flushing it to disk */
454 uchar *pos;
455 if (info->type == WRITE_CACHE && type == READ_CACHE)
456 {
457 info->read_end=info->write_pos;
458 info->end_of_file=my_b_tell(info);
459 /* Ensure we will read all data */
460 info->myflags|= MY_FULL_IO;
461 /*
462 Trigger a new seek only if we have a valid
463 file handle.
464 */
465 info->seek_not_done= (info->file != -1);
466 }
467 else if (type == WRITE_CACHE)
468 {
469 if (info->type == READ_CACHE)
470 {
471 info->write_end=info->write_buffer+info->buffer_length;
472 info->seek_not_done=1;
473 }
474 info->end_of_file = ~(my_off_t) 0;
475 info->myflags&= ~MY_FULL_IO;
476 }
477 pos=info->request_pos+(seek_offset-info->pos_in_file);
478 if (type == WRITE_CACHE)
479 info->write_pos=pos;
480 else
481 info->read_pos= pos;
482#ifdef HAVE_AIOWAIT
483 my_aiowait(&info->aio_result); /* Wait for outstanding req */
484#endif
485 }
486 else
487 {
488 /*
489 If we change from WRITE_CACHE to READ_CACHE, assume that everything
490 after the current positions should be ignored
491 */
492 if (info->type == WRITE_CACHE && type == READ_CACHE)
493 info->end_of_file=my_b_tell(info);
494 /* flush cache if we want to reuse it */
495 if (!clear_cache && my_b_flush_io_cache(info,1))
496 DBUG_RETURN(1);
497 info->pos_in_file=seek_offset;
498 /* Better to do always do a seek */
499 info->seek_not_done=1;
500 info->request_pos=info->read_pos=info->write_pos=info->buffer;
501 if (type == READ_CACHE)
502 {
503 info->read_end=info->buffer; /* Nothing in cache */
504 }
505 else
506 {
507 if (info->myflags & MY_ENCRYPT)
508 {
509 info->write_end = info->write_buffer + info->buffer_length;
510 if (seek_offset && info->file != -1)
511 {
512 info->read_end= info->buffer;
513 _my_b_encr_read(info, 0, 0); /* prefill the buffer */
514 info->write_pos= info->read_pos;
515 info->seek_not_done=1;
516 }
517 }
518 else
519 {
520 info->write_end=(info->buffer + info->buffer_length -
521 (seek_offset & (IO_SIZE-1)));
522 }
523 info->end_of_file= ~(my_off_t) 0;
524 }
525 }
526 info->type=type;
527 info->error=0;
528 init_functions(info);
529
530#ifdef HAVE_AIOWAIT
531 if (use_async_io && ! my_disable_async_io &&
532 ((ulong) info->buffer_length <
533 (ulong) (info->end_of_file - seek_offset)))
534 {
535 DBUG_ASSERT(!(cache_myflags & MY_ENCRYPT));
536 info->read_length=info->buffer_length/2;
537 info->read_function=_my_b_async_read;
538 }
539 info->inited=0;
540#endif
541 DBUG_RETURN(0);
542} /* reinit_io_cache */
543
544
545int _my_b_read(IO_CACHE *info, uchar *Buffer, size_t Count)
546{
547 size_t left_length;
548 int res;
549
550 /* If the buffer is not empty yet, copy what is available. */
551 if ((left_length= (size_t) (info->read_end - info->read_pos)))
552 {
553 DBUG_ASSERT(Count > left_length);
554 memcpy(Buffer, info->read_pos, left_length);
555 Buffer+=left_length;
556 Count-=left_length;
557 }
558 res= info->read_function(info, Buffer, Count);
559 if (res && info->error >= 0)
560 info->error+= (int)left_length; /* update number or read bytes */
561 return res;
562}
563
564int _my_b_write(IO_CACHE *info, const uchar *Buffer, size_t Count)
565{
566 size_t rest_length;
567 int res;
568
569 /* Always use my_b_flush_io_cache() to flush write_buffer! */
570 DBUG_ASSERT(Buffer != info->write_buffer);
571
572 if (info->pos_in_file + info->buffer_length > info->end_of_file)
573 {
574 my_errno=errno=EFBIG;
575 return info->error = -1;
576 }
577
578 rest_length= (size_t) (info->write_end - info->write_pos);
579 DBUG_ASSERT(Count >= rest_length);
580 memcpy(info->write_pos, Buffer, (size_t) rest_length);
581 Buffer+=rest_length;
582 Count-=rest_length;
583 info->write_pos+=rest_length;
584
585 if (my_b_flush_io_cache(info, 1))
586 return 1;
587
588 if (Count)
589 {
590 my_off_t old_pos_in_file= info->pos_in_file;
591 res= info->write_function(info, Buffer, Count);
592 Count-= (size_t) (info->pos_in_file - old_pos_in_file);
593 Buffer+= info->pos_in_file - old_pos_in_file;
594 }
595 else
596 res= 0;
597
598 if (!res && Count)
599 {
600 memcpy(info->write_pos, Buffer, Count);
601 info->write_pos+= Count;
602 }
603 return res;
604}
605
606/*
607 Read buffered.
608
609 SYNOPSIS
610 _my_b_cache_read()
611 info IO_CACHE pointer
612 Buffer Buffer to retrieve count bytes from file
613 Count Number of bytes to read into Buffer
614
615 NOTE
616 This function is only called from the my_b_read() macro when there
617 isn't enough characters in the buffer to satisfy the request.
618
619 WARNING
620
621 When changing this function, be careful with handling file offsets
622 (end-of_file, pos_in_file). Do not cast them to possibly smaller
623 types than my_off_t unless you can be sure that their value fits.
624 Same applies to differences of file offsets.
625
626 When changing this function, check _my_b_cache_read_r(). It might need the
627 same change.
628
629 RETURN
630 0 we succeeded in reading all data
631 1 Error: couldn't read requested characters. In this case:
632 If info->error == -1, we got a read error.
633 Otherwise info->error contains the number of bytes in Buffer.
634*/
635
636int _my_b_cache_read(IO_CACHE *info, uchar *Buffer, size_t Count)
637{
638 size_t length, diff_length, left_length= 0, max_length;
639 my_off_t pos_in_file;
640 DBUG_ENTER("_my_b_cache_read");
641
642 /* pos_in_file always point on where info->buffer was read */
643 pos_in_file=info->pos_in_file+ (size_t) (info->read_end - info->buffer);
644
645 /*
646 Whenever a function which operates on IO_CACHE flushes/writes
647 some part of the IO_CACHE to disk it will set the property
648 "seek_not_done" to indicate this to other functions operating
649 on the IO_CACHE.
650 */
651 if (info->seek_not_done)
652 {
653 if ((mysql_file_seek(info->file, pos_in_file, MY_SEEK_SET, MYF(0))
654 != MY_FILEPOS_ERROR))
655 {
656 /* No error, reset seek_not_done flag. */
657 info->seek_not_done= 0;
658
659 if (info->next_file_user)
660 {
661 IO_CACHE *c;
662 for (c= info->next_file_user;
663 c!= info;
664 c= c->next_file_user)
665 {
666 c->seek_not_done= 1;
667 }
668 }
669 }
670 else
671 {
672 /*
673 If the seek failed and the error number is ESPIPE, it is because
674 info->file is a pipe or socket or FIFO. We never should have tried
675 to seek on that. See Bugs#25807 and #22828 for more info.
676 */
677 DBUG_ASSERT(my_errno != ESPIPE);
678 info->error= -1;
679 DBUG_RETURN(1);
680 }
681 }
682
683 /*
684 Calculate, how much we are within a IO_SIZE block. Ideally this
685 should be zero.
686 */
687 diff_length= (size_t) (pos_in_file & (IO_SIZE-1));
688
689 /*
690 If more than a block plus the rest of the current block is wanted,
691 we do read directly, without filling the buffer.
692 */
693 if (Count >= (size_t) (IO_SIZE+(IO_SIZE-diff_length)))
694 { /* Fill first intern buffer */
695 size_t read_length;
696 if (info->end_of_file <= pos_in_file)
697 {
698 /* End of file. Return, what we did copy from the buffer. */
699 info->error= (int) left_length;
700 info->seek_not_done=1;
701 DBUG_RETURN(1);
702 }
703 /*
704 Crop the wanted count to a multiple of IO_SIZE and subtract,
705 what we did already read from a block. That way, the read will
706 end aligned with a block.
707 */
708 length= IO_ROUND_DN(Count) - diff_length;
709 if ((read_length= mysql_file_read(info->file,Buffer, length, info->myflags))
710 != length)
711 {
712 /*
713 If we didn't get, what we wanted, we either return -1 for a read
714 error, or (it's end of file), how much we got in total.
715 */
716 info->error= (read_length == (size_t) -1 ? -1 :
717 (int) (read_length+left_length));
718 info->seek_not_done=1;
719 DBUG_RETURN(1);
720 }
721 Count-=length;
722 Buffer+=length;
723 pos_in_file+=length;
724 left_length+=length;
725 diff_length=0;
726 }
727
728 /*
729 At this point, we want less than one and a partial block.
730 We will read a full cache, minus the number of bytes, we are
731 within a block already. So we will reach new alignment.
732 */
733 max_length= info->read_length-diff_length;
734 /* We will not read past end of file. */
735 if (info->type != READ_FIFO &&
736 max_length > (info->end_of_file - pos_in_file))
737 max_length= (size_t) (info->end_of_file - pos_in_file);
738 /*
739 If there is nothing left to read,
740 we either are done, or we failed to fulfill the request.
741 Otherwise, we read max_length into the cache.
742 */
743 if (!max_length)
744 {
745 if (Count)
746 {
747 /* We couldn't fulfil the request. Return, how much we got. */
748 info->error= (int) left_length;
749 DBUG_RETURN(1);
750 }
751 else
752 {
753 info->error= 0;
754 DBUG_RETURN(0); /* EOF */
755 }
756 }
757 else
758 {
759 if (info->next_file_user)
760 {
761 IO_CACHE *c;
762 for (c= info->next_file_user;
763 c!= info;
764 c= c->next_file_user)
765 {
766 c->seek_not_done= 1;
767 }
768 }
769 if ((length= mysql_file_read(info->file,info->buffer, max_length,
770 info->myflags)) < Count ||
771 length == (size_t) -1)
772 {
773 /*
774 We got an read error, or less than requested (end of file).
775 If not a read error, copy, what we got.
776 */
777 if (length != (size_t) -1)
778 memcpy(Buffer, info->buffer, length);
779 info->pos_in_file= pos_in_file;
780 /* For a read error, return -1, otherwise, what we got in total. */
781 info->error= length == (size_t) -1 ? -1 : (int) (length+left_length);
782 info->read_pos=info->read_end=info->buffer;
783 info->seek_not_done=1;
784 DBUG_RETURN(1);
785 }
786 }
787 /*
788 Count is the remaining number of bytes requested.
789 length is the amount of data in the cache.
790 Read Count bytes from the cache.
791 */
792 info->read_pos=info->buffer+Count;
793 info->read_end=info->buffer+length;
794 info->pos_in_file=pos_in_file;
795 memcpy(Buffer, info->buffer, Count);
796 DBUG_RETURN(0);
797}
798
799
800/*
801 Prepare IO_CACHE for shared use.
802
803 SYNOPSIS
804 init_io_cache_share()
805 read_cache A read cache. This will be copied for
806 every thread after setup.
807 cshare The share.
808 write_cache If non-NULL a write cache that is to be
809 synchronized with the read caches.
810 num_threads Number of threads sharing the cache
811 including the write thread if any.
812
813 DESCRIPTION
814
815 The shared cache is used so: One IO_CACHE is initialized with
816 init_io_cache(). This includes the allocation of a buffer. Then a
817 share is allocated and init_io_cache_share() is called with the io
818 cache and the share. Then the io cache is copied for each thread. So
819 every thread has its own copy of IO_CACHE. But the allocated buffer
820 is shared because cache->buffer is the same for all caches.
821
822 One thread reads data from the file into the buffer. All threads
823 read from the buffer, but every thread maintains its own set of
824 pointers into the buffer. When all threads have used up the buffer
825 contents, one of the threads reads the next block of data into the
826 buffer. To accomplish this, each thread enters the cache lock before
827 accessing the buffer. They wait in lock_io_cache() until all threads
828 joined the lock. The last thread entering the lock is in charge of
829 reading from file to buffer. It wakes all threads when done.
830
831 Synchronizing a write cache to the read caches works so: Whenever
832 the write buffer needs a flush, the write thread enters the lock and
833 waits for all other threads to enter the lock too. They do this when
834 they have used up the read buffer. When all threads are in the lock,
835 the write thread copies the write buffer to the read buffer and
836 wakes all threads.
837
838 share->running_threads is the number of threads not being in the
839 cache lock. When entering lock_io_cache() the number is decreased.
840 When the thread that fills the buffer enters unlock_io_cache() the
841 number is reset to the number of threads. The condition
842 running_threads == 0 means that all threads are in the lock. Bumping
843 up the number to the full count is non-intuitive. But increasing the
844 number by one for each thread that leaves the lock could lead to a
845 solo run of one thread. The last thread to join a lock reads from
846 file to buffer, wakes the other threads, processes the data in the
847 cache and enters the lock again. If no other thread left the lock
848 meanwhile, it would think it's the last one again and read the next
849 block...
850
851 The share has copies of 'error', 'buffer', 'read_end', and
852 'pos_in_file' from the thread that filled the buffer. We may not be
853 able to access this information directly from its cache because the
854 thread may be removed from the share before the variables could be
855 copied by all other threads. Or, if a write buffer is synchronized,
856 it would change its 'pos_in_file' after waking the other threads,
857 possibly before they could copy its value.
858
859 However, the 'buffer' variable in the share is for a synchronized
860 write cache. It needs to know where to put the data. Otherwise it
861 would need access to the read cache of one of the threads that is
862 not yet removed from the share.
863
864 RETURN
865 void
866*/
867
868void init_io_cache_share(IO_CACHE *read_cache, IO_CACHE_SHARE *cshare,
869 IO_CACHE *write_cache, uint num_threads)
870{
871 DBUG_ENTER("init_io_cache_share");
872 DBUG_PRINT("io_cache_share", ("read_cache: %p share: %p "
873 "write_cache: %p threads: %u",
874 read_cache, cshare,
875 write_cache, num_threads));
876
877 DBUG_ASSERT(num_threads > 1);
878 DBUG_ASSERT(read_cache->type == READ_CACHE);
879 DBUG_ASSERT(!write_cache || (write_cache->type == WRITE_CACHE));
880
881 mysql_mutex_init(key_IO_CACHE_SHARE_mutex,
882 &cshare->mutex, MY_MUTEX_INIT_FAST);
883 mysql_cond_init(key_IO_CACHE_SHARE_cond, &cshare->cond, 0);
884 mysql_cond_init(key_IO_CACHE_SHARE_cond_writer, &cshare->cond_writer, 0);
885
886 cshare->running_threads= num_threads;
887 cshare->total_threads= num_threads;
888 cshare->error= 0; /* Initialize. */
889 cshare->buffer= read_cache->buffer;
890 cshare->read_end= NULL; /* See function comment of lock_io_cache(). */
891 cshare->pos_in_file= 0; /* See function comment of lock_io_cache(). */
892 cshare->source_cache= write_cache; /* Can be NULL. */
893
894 read_cache->share= cshare;
895 read_cache->read_function= _my_b_cache_read_r;
896
897 if (write_cache)
898 {
899 write_cache->share= cshare;
900 write_cache->write_function= _my_b_cache_write_r;
901 }
902
903 DBUG_VOID_RETURN;
904}
905
906
907/*
908 Remove a thread from shared access to IO_CACHE.
909
910 SYNOPSIS
911 remove_io_thread()
912 cache The IO_CACHE to be removed from the share.
913
914 NOTE
915
916 Every thread must do that on exit for not to deadlock other threads.
917
918 The last thread destroys the pthread resources.
919
920 A writer flushes its cache first.
921
922 RETURN
923 void
924*/
925
926void remove_io_thread(IO_CACHE *cache)
927{
928 IO_CACHE_SHARE *cshare= cache->share;
929 uint total;
930 DBUG_ENTER("remove_io_thread");
931
932 /* If the writer goes, it needs to flush the write cache. */
933 if (cache == cshare->source_cache)
934 flush_io_cache(cache);
935
936 mysql_mutex_lock(&cshare->mutex);
937 DBUG_PRINT("io_cache_share", ("%s: %p",
938 (cache == cshare->source_cache) ?
939 "writer" : "reader", cache));
940
941 /* Remove from share. */
942 total= --cshare->total_threads;
943 DBUG_PRINT("io_cache_share", ("remaining threads: %u", total));
944
945 /* Detach from share. */
946 cache->share= NULL;
947
948 /* If the writer goes, let the readers know. */
949 if (cache == cshare->source_cache)
950 {
951 DBUG_PRINT("io_cache_share", ("writer leaves"));
952 cshare->source_cache= NULL;
953 }
954
955 /* If all threads are waiting for me to join the lock, wake them. */
956 if (!--cshare->running_threads)
957 {
958 DBUG_PRINT("io_cache_share", ("the last running thread leaves, wake all"));
959 mysql_cond_signal(&cshare->cond_writer);
960 mysql_cond_broadcast(&cshare->cond);
961 }
962
963 mysql_mutex_unlock(&cshare->mutex);
964
965 if (!total)
966 {
967 DBUG_PRINT("io_cache_share", ("last thread removed, destroy share"));
968 mysql_cond_destroy (&cshare->cond_writer);
969 mysql_cond_destroy (&cshare->cond);
970 mysql_mutex_destroy(&cshare->mutex);
971 }
972
973 DBUG_VOID_RETURN;
974}
975
976
977/*
978 Lock IO cache and wait for all other threads to join.
979
980 SYNOPSIS
981 lock_io_cache()
982 cache The cache of the thread entering the lock.
983 pos File position of the block to read.
984 Unused for the write thread.
985
986 DESCRIPTION
987
988 Wait for all threads to finish with the current buffer. We want
989 all threads to proceed in concert. The last thread to join
990 lock_io_cache() will read the block from file and all threads start
991 to use it. Then they will join again for reading the next block.
992
993 The waiting threads detect a fresh buffer by comparing
994 cshare->pos_in_file with the position they want to process next.
995 Since the first block may start at position 0, we take
996 cshare->read_end as an additional condition. This variable is
997 initialized to NULL and will be set after a block of data is written
998 to the buffer.
999
1000 RETURN
1001 1 OK, lock in place, go ahead and read.
1002 0 OK, unlocked, another thread did the read.
1003*/
1004
1005static int lock_io_cache(IO_CACHE *cache, my_off_t pos)
1006{
1007 IO_CACHE_SHARE *cshare= cache->share;
1008 DBUG_ENTER("lock_io_cache");
1009
1010 /* Enter the lock. */
1011 mysql_mutex_lock(&cshare->mutex);
1012 cshare->running_threads--;
1013 DBUG_PRINT("io_cache_share", ("%s: %p pos: %lu running: %u",
1014 (cache == cshare->source_cache) ?
1015 "writer" : "reader", cache, (ulong) pos,
1016 cshare->running_threads));
1017
1018 if (cshare->source_cache)
1019 {
1020 /* A write cache is synchronized to the read caches. */
1021
1022 if (cache == cshare->source_cache)
1023 {
1024 /* The writer waits until all readers are here. */
1025 while (cshare->running_threads)
1026 {
1027 DBUG_PRINT("io_cache_share", ("writer waits in lock"));
1028 mysql_cond_wait(&cshare->cond_writer, &cshare->mutex);
1029 }
1030 DBUG_PRINT("io_cache_share", ("writer awoke, going to copy"));
1031
1032 /* Stay locked. Leave the lock later by unlock_io_cache(). */
1033 DBUG_RETURN(1);
1034 }
1035
1036 /* The last thread wakes the writer. */
1037 if (!cshare->running_threads)
1038 {
1039 DBUG_PRINT("io_cache_share", ("waking writer"));
1040 mysql_cond_signal(&cshare->cond_writer);
1041 }
1042
1043 /*
1044 Readers wait until the data is copied from the writer. Another
1045 reason to stop waiting is the removal of the write thread. If this
1046 happens, we leave the lock with old data in the buffer.
1047 */
1048 while ((!cshare->read_end || (cshare->pos_in_file < pos)) &&
1049 cshare->source_cache)
1050 {
1051 DBUG_PRINT("io_cache_share", ("reader waits in lock"));
1052 mysql_cond_wait(&cshare->cond, &cshare->mutex);
1053 }
1054
1055 /*
1056 If the writer was removed from the share while this thread was
1057 asleep, we need to simulate an EOF condition. The writer cannot
1058 reset the share variables as they might still be in use by readers
1059 of the last block. When we awake here then because the last
1060 joining thread signalled us. If the writer is not the last, it
1061 will not signal. So it is safe to clear the buffer here.
1062 */
1063 if (!cshare->read_end || (cshare->pos_in_file < pos))
1064 {
1065 DBUG_PRINT("io_cache_share", ("reader found writer removed. EOF"));
1066 cshare->read_end= cshare->buffer; /* Empty buffer. */
1067 cshare->error= 0; /* EOF is not an error. */
1068 }
1069 }
1070 else
1071 {
1072 /*
1073 There are read caches only. The last thread arriving in
1074 lock_io_cache() continues with a locked cache and reads the block.
1075 */
1076 if (!cshare->running_threads)
1077 {
1078 DBUG_PRINT("io_cache_share", ("last thread joined, going to read"));
1079 /* Stay locked. Leave the lock later by unlock_io_cache(). */
1080 DBUG_RETURN(1);
1081 }
1082
1083 /*
1084 All other threads wait until the requested block is read by the
1085 last thread arriving. Another reason to stop waiting is the
1086 removal of a thread. If this leads to all threads being in the
1087 lock, we have to continue also. The first of the awaken threads
1088 will then do the read.
1089 */
1090 while ((!cshare->read_end || (cshare->pos_in_file < pos)) &&
1091 cshare->running_threads)
1092 {
1093 DBUG_PRINT("io_cache_share", ("reader waits in lock"));
1094 mysql_cond_wait(&cshare->cond, &cshare->mutex);
1095 }
1096
1097 /* If the block is not yet read, continue with a locked cache and read. */
1098 if (!cshare->read_end || (cshare->pos_in_file < pos))
1099 {
1100 DBUG_PRINT("io_cache_share", ("reader awoke, going to read"));
1101 /* Stay locked. Leave the lock later by unlock_io_cache(). */
1102 DBUG_RETURN(1);
1103 }
1104
1105 /* Another thread did read the block already. */
1106 }
1107 DBUG_PRINT("io_cache_share", ("reader awoke, going to process %u bytes",
1108 (uint) (cshare->read_end ? (size_t)
1109 (cshare->read_end - cshare->buffer) :
1110 0)));
1111
1112 /*
1113 Leave the lock. Do not call unlock_io_cache() later. The thread that
1114 filled the buffer did this and marked all threads as running.
1115 */
1116 mysql_mutex_unlock(&cshare->mutex);
1117 DBUG_RETURN(0);
1118}
1119
1120
1121/*
1122 Unlock IO cache.
1123
1124 SYNOPSIS
1125 unlock_io_cache()
1126 cache The cache of the thread leaving the lock.
1127
1128 NOTE
1129 This is called by the thread that filled the buffer. It marks all
1130 threads as running and awakes them. This must not be done by any
1131 other thread.
1132
1133 Do not signal cond_writer. Either there is no writer or the writer
1134 is the only one who can call this function.
1135
1136 The reason for resetting running_threads to total_threads before
1137 waking all other threads is that it could be possible that this
1138 thread is so fast with processing the buffer that it enters the lock
1139 before even one other thread has left it. If every awoken thread
1140 would increase running_threads by one, this thread could think that
1141 he is again the last to join and would not wait for the other
1142 threads to process the data.
1143
1144 RETURN
1145 void
1146*/
1147
1148static void unlock_io_cache(IO_CACHE *cache)
1149{
1150 IO_CACHE_SHARE *cshare= cache->share;
1151 DBUG_ENTER("unlock_io_cache");
1152 DBUG_PRINT("io_cache_share", ("%s: %p pos: %lu running: %u",
1153 (cache == cshare->source_cache) ?
1154 "writer" : "reader",
1155 cache, (ulong) cshare->pos_in_file,
1156 cshare->total_threads));
1157
1158 cshare->running_threads= cshare->total_threads;
1159 mysql_cond_broadcast(&cshare->cond);
1160 mysql_mutex_unlock(&cshare->mutex);
1161 DBUG_VOID_RETURN;
1162}
1163
1164
1165/*
1166 Read from IO_CACHE when it is shared between several threads.
1167
1168 SYNOPSIS
1169 _my_b_cache_read_r()
1170 cache IO_CACHE pointer
1171 Buffer Buffer to retrieve count bytes from file
1172 Count Number of bytes to read into Buffer
1173
1174 NOTE
1175 This function is only called from the my_b_read() macro when there
1176 isn't enough characters in the buffer to satisfy the request.
1177
1178 IMPLEMENTATION
1179
1180 It works as follows: when a thread tries to read from a file (that
1181 is, after using all the data from the (shared) buffer), it just
1182 hangs on lock_io_cache(), waiting for other threads. When the very
1183 last thread attempts a read, lock_io_cache() returns 1, the thread
1184 does actual IO and unlock_io_cache(), which signals all the waiting
1185 threads that data is in the buffer.
1186
1187 WARNING
1188
1189 When changing this function, be careful with handling file offsets
1190 (end-of_file, pos_in_file). Do not cast them to possibly smaller
1191 types than my_off_t unless you can be sure that their value fits.
1192 Same applies to differences of file offsets. (Bug #11527)
1193
1194 When changing this function, check _my_b_cache_read(). It might need the
1195 same change.
1196
1197 RETURN
1198 0 we succeeded in reading all data
1199 1 Error: can't read requested characters
1200*/
1201
1202static int _my_b_cache_read_r(IO_CACHE *cache, uchar *Buffer, size_t Count)
1203{
1204 my_off_t pos_in_file;
1205 size_t length, diff_length, left_length= 0;
1206 IO_CACHE_SHARE *cshare= cache->share;
1207 DBUG_ENTER("_my_b_cache_read_r");
1208 DBUG_ASSERT(!(cache->myflags & MY_ENCRYPT));
1209
1210 while (Count)
1211 {
1212 size_t cnt, len;
1213
1214 pos_in_file= cache->pos_in_file + (cache->read_end - cache->buffer);
1215 diff_length= (size_t) (pos_in_file & (IO_SIZE-1));
1216 length=IO_ROUND_UP(Count+diff_length)-diff_length;
1217 length= ((length <= cache->read_length) ?
1218 length + IO_ROUND_DN(cache->read_length - length) :
1219 length - IO_ROUND_UP(length - cache->read_length));
1220 if (cache->type != READ_FIFO &&
1221 (length > (cache->end_of_file - pos_in_file)))
1222 length= (size_t) (cache->end_of_file - pos_in_file);
1223 if (length == 0)
1224 {
1225 cache->error= (int) left_length;
1226 DBUG_RETURN(1);
1227 }
1228 if (lock_io_cache(cache, pos_in_file))
1229 {
1230 /* With a synchronized write/read cache we won't come here... */
1231 DBUG_ASSERT(!cshare->source_cache);
1232 /*
1233 ... unless the writer has gone before this thread entered the
1234 lock. Simulate EOF in this case. It can be distinguished by
1235 cache->file.
1236 */
1237 if (cache->file < 0)
1238 len= 0;
1239 else
1240 {
1241 /*
1242 Whenever a function which operates on IO_CACHE flushes/writes
1243 some part of the IO_CACHE to disk it will set the property
1244 "seek_not_done" to indicate this to other functions operating
1245 on the IO_CACHE.
1246 */
1247 if (cache->seek_not_done)
1248 {
1249 if (mysql_file_seek(cache->file, pos_in_file, MY_SEEK_SET, MYF(0))
1250 == MY_FILEPOS_ERROR)
1251 {
1252 cache->error= -1;
1253 unlock_io_cache(cache);
1254 DBUG_RETURN(1);
1255 }
1256 }
1257 len= mysql_file_read(cache->file, cache->buffer, length, cache->myflags);
1258 }
1259 DBUG_PRINT("io_cache_share", ("read %lu bytes", (ulong) len));
1260
1261 cache->read_end= cache->buffer + (len == (size_t) -1 ? 0 : len);
1262 cache->error= (len == length ? 0 : (int) len);
1263 cache->pos_in_file= pos_in_file;
1264
1265 /* Copy important values to the share. */
1266 cshare->error= cache->error;
1267 cshare->read_end= cache->read_end;
1268 cshare->pos_in_file= pos_in_file;
1269
1270 /* Mark all threads as running and wake them. */
1271 unlock_io_cache(cache);
1272 }
1273 else
1274 {
1275 /*
1276 With a synchronized write/read cache readers always come here.
1277 Copy important values from the share.
1278 */
1279 cache->error= cshare->error;
1280 cache->read_end= cshare->read_end;
1281 cache->pos_in_file= cshare->pos_in_file;
1282
1283 len= ((cache->error == -1) ? (size_t) -1 :
1284 (size_t) (cache->read_end - cache->buffer));
1285 }
1286 cache->read_pos= cache->buffer;
1287 cache->seek_not_done= 0;
1288 if (len == 0 || len == (size_t) -1)
1289 {
1290 DBUG_PRINT("io_cache_share", ("reader error. len %lu left %lu",
1291 (ulong) len, (ulong) left_length));
1292 cache->error= (int) left_length;
1293 DBUG_RETURN(1);
1294 }
1295 cnt= (len > Count) ? Count : len;
1296 memcpy(Buffer, cache->read_pos, cnt);
1297 Count -= cnt;
1298 Buffer+= cnt;
1299 left_length+= cnt;
1300 cache->read_pos+= cnt;
1301 }
1302 DBUG_RETURN(0);
1303}
1304
1305
1306/*
1307 Copy data from write cache to read cache.
1308
1309 SYNOPSIS
1310 copy_to_read_buffer()
1311 write_cache The write cache.
1312 write_buffer The source of data, mostly the cache buffer.
1313 write_length The number of bytes to copy.
1314
1315 NOTE
1316 The write thread will wait for all read threads to join the cache
1317 lock. Then it copies the data over and wakes the read threads.
1318
1319 RETURN
1320 void
1321*/
1322
1323static void copy_to_read_buffer(IO_CACHE *write_cache,
1324 const uchar *write_buffer, my_off_t pos_in_file)
1325{
1326 size_t write_length= (size_t) (write_cache->pos_in_file - pos_in_file);
1327 IO_CACHE_SHARE *cshare= write_cache->share;
1328
1329 DBUG_ASSERT(cshare->source_cache == write_cache);
1330 /*
1331 write_length is usually less or equal to buffer_length.
1332 It can be bigger if _my_b_cache_write_r() is called with a big length.
1333 */
1334 while (write_length)
1335 {
1336 size_t copy_length= MY_MIN(write_length, write_cache->buffer_length);
1337 int __attribute__((unused)) rc;
1338
1339 rc= lock_io_cache(write_cache, pos_in_file);
1340 /* The writing thread does always have the lock when it awakes. */
1341 DBUG_ASSERT(rc);
1342
1343 memcpy(cshare->buffer, write_buffer, copy_length);
1344
1345 cshare->error= 0;
1346 cshare->read_end= cshare->buffer + copy_length;
1347 cshare->pos_in_file= pos_in_file;
1348
1349 /* Mark all threads as running and wake them. */
1350 unlock_io_cache(write_cache);
1351
1352 write_buffer+= copy_length;
1353 write_length-= copy_length;
1354 }
1355}
1356
1357
1358/*
1359 Do sequential read from the SEQ_READ_APPEND cache.
1360
1361 We do this in three stages:
1362 - first read from info->buffer
1363 - then if there are still data to read, try the file descriptor
1364 - afterwards, if there are still data to read, try append buffer
1365
1366 RETURNS
1367 0 Success
1368 1 Failed to read
1369*/
1370
1371static int _my_b_seq_read(IO_CACHE *info, uchar *Buffer, size_t Count)
1372{
1373 size_t length, diff_length, left_length= 0, save_count, max_length;
1374 my_off_t pos_in_file;
1375 save_count=Count;
1376
1377 lock_append_buffer(info);
1378
1379 /* pos_in_file always point on where info->buffer was read */
1380 if ((pos_in_file=info->pos_in_file +
1381 (size_t) (info->read_end - info->buffer)) >= info->end_of_file)
1382 goto read_append_buffer;
1383
1384 /*
1385 With read-append cache we must always do a seek before we read,
1386 because the write could have moved the file pointer astray
1387 */
1388 if (mysql_file_seek(info->file, pos_in_file, MY_SEEK_SET, MYF(0)) == MY_FILEPOS_ERROR)
1389 {
1390 info->error= -1;
1391 unlock_append_buffer(info);
1392 return (1);
1393 }
1394 info->seek_not_done=0;
1395
1396 diff_length= (size_t) (pos_in_file & (IO_SIZE-1));
1397
1398 /* now the second stage begins - read from file descriptor */
1399 if (Count >= (size_t) (IO_SIZE+(IO_SIZE-diff_length)))
1400 {
1401 /* Fill first intern buffer */
1402 size_t read_length;
1403
1404 length= IO_ROUND_DN(Count) - diff_length;
1405 if ((read_length= mysql_file_read(info->file,Buffer, length,
1406 info->myflags)) == (size_t) -1)
1407 {
1408 info->error= -1;
1409 unlock_append_buffer(info);
1410 return 1;
1411 }
1412 Count-=read_length;
1413 Buffer+=read_length;
1414 pos_in_file+=read_length;
1415
1416 if (read_length != length)
1417 {
1418 /*
1419 We only got part of data; Read the rest of the data from the
1420 write buffer
1421 */
1422 goto read_append_buffer;
1423 }
1424 left_length+=length;
1425 diff_length=0;
1426 }
1427
1428 max_length= info->read_length-diff_length;
1429 if (max_length > (info->end_of_file - pos_in_file))
1430 max_length= (size_t) (info->end_of_file - pos_in_file);
1431 if (!max_length)
1432 {
1433 if (Count)
1434 goto read_append_buffer;
1435 length=0; /* Didn't read any more chars */
1436 }
1437 else
1438 {
1439 length= mysql_file_read(info->file,info->buffer, max_length, info->myflags);
1440 if (length == (size_t) -1)
1441 {
1442 info->error= -1;
1443 unlock_append_buffer(info);
1444 return 1;
1445 }
1446 if (length < Count)
1447 {
1448 memcpy(Buffer, info->buffer, length);
1449 Count -= length;
1450 Buffer += length;
1451
1452 /*
1453 added the line below to make
1454 DBUG_ASSERT(pos_in_file==info->end_of_file) pass.
1455 otherwise this does not appear to be needed
1456 */
1457 pos_in_file += length;
1458 goto read_append_buffer;
1459 }
1460 }
1461 unlock_append_buffer(info);
1462 info->read_pos=info->buffer+Count;
1463 info->read_end=info->buffer+length;
1464 info->pos_in_file=pos_in_file;
1465 memcpy(Buffer,info->buffer,(size_t) Count);
1466 return 0;
1467
1468read_append_buffer:
1469
1470 /*
1471 Read data from the current write buffer.
1472 Count should never be == 0 here (The code will work even if count is 0)
1473 */
1474
1475 {
1476 /* First copy the data to Count */
1477 size_t len_in_buff = (size_t) (info->write_pos - info->append_read_pos);
1478 size_t copy_len;
1479 size_t transfer_len;
1480
1481 DBUG_ASSERT(info->append_read_pos <= info->write_pos);
1482 copy_len=MY_MIN(Count, len_in_buff);
1483 memcpy(Buffer, info->append_read_pos, copy_len);
1484 info->append_read_pos += copy_len;
1485 Count -= copy_len;
1486 if (Count)
1487 info->error= (int) (save_count - Count);
1488
1489 /* Fill read buffer with data from write buffer */
1490 memcpy(info->buffer, info->append_read_pos,
1491 (size_t) (transfer_len=len_in_buff - copy_len));
1492 info->read_pos= info->buffer;
1493 info->read_end= info->buffer+transfer_len;
1494 info->append_read_pos=info->write_pos;
1495 info->pos_in_file=pos_in_file+copy_len;
1496 info->end_of_file+=len_in_buff;
1497 }
1498 unlock_append_buffer(info);
1499 return Count ? 1 : 0;
1500}
1501
1502
1503#ifdef HAVE_AIOWAIT
1504
1505/*
1506 Read from the IO_CACHE into a buffer and feed asynchronously
1507 from disk when needed.
1508
1509 SYNOPSIS
1510 _my_b_async_read()
1511 info IO_CACHE pointer
1512 Buffer Buffer to retrieve count bytes from file
1513 Count Number of bytes to read into Buffer
1514
1515 RETURN VALUE
1516 -1 An error has occurred; my_errno is set.
1517 0 Success
1518 1 An error has occurred; IO_CACHE to error state.
1519*/
1520
1521int _my_b_async_read(IO_CACHE *info, uchar *Buffer, size_t Count)
1522{
1523 size_t length, read_length, diff_length, left_length=0, use_length, org_Count;
1524 size_t max_length;
1525 my_off_t next_pos_in_file;
1526 uchar *read_buffer;
1527
1528 org_Count=Count;
1529
1530 if (info->inited)
1531 { /* wait for read block */
1532 info->inited=0; /* No more block to read */
1533 my_aiowait(&info->aio_result); /* Wait for outstanding req */
1534 if (info->aio_result.result.aio_errno)
1535 {
1536 if (info->myflags & MY_WME)
1537 my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG),
1538 my_filename(info->file),
1539 info->aio_result.result.aio_errno);
1540 my_errno=info->aio_result.result.aio_errno;
1541 info->error= -1;
1542 return(1);
1543 }
1544 if (! (read_length= (size_t) info->aio_result.result.aio_return) ||
1545 read_length == (size_t) -1)
1546 {
1547 my_errno=0; /* For testing */
1548 info->error= (read_length == (size_t) -1 ? -1 :
1549 (int) (read_length+left_length));
1550 return(1);
1551 }
1552 info->pos_in_file+= (size_t) (info->read_end - info->request_pos);
1553
1554 if (info->request_pos != info->buffer)
1555 info->request_pos=info->buffer;
1556 else
1557 info->request_pos=info->buffer+info->read_length;
1558 info->read_pos=info->request_pos;
1559 next_pos_in_file=info->aio_read_pos+read_length;
1560
1561 /* Check if pos_in_file is changed
1562 (_ni_read_cache may have skipped some bytes) */
1563
1564 if (info->aio_read_pos < info->pos_in_file)
1565 { /* Fix if skipped bytes */
1566 if (info->aio_read_pos + read_length < info->pos_in_file)
1567 {
1568 read_length=0; /* Skip block */
1569 next_pos_in_file=info->pos_in_file;
1570 }
1571 else
1572 {
1573 my_off_t offset= (info->pos_in_file - info->aio_read_pos);
1574 info->pos_in_file=info->aio_read_pos; /* Whe are here */
1575 info->read_pos=info->request_pos+offset;
1576 read_length-=offset; /* Bytes left from read_pos */
1577 }
1578 }
1579#ifndef DBUG_OFF
1580 if (info->aio_read_pos > info->pos_in_file)
1581 {
1582 my_errno=EINVAL;
1583 return(info->read_length= (size_t) -1);
1584 }
1585#endif
1586 /* Copy found bytes to buffer */
1587 length=MY_MIN(Count,read_length);
1588 memcpy(Buffer,info->read_pos,(size_t) length);
1589 Buffer+=length;
1590 Count-=length;
1591 left_length+=length;
1592 info->read_end=info->rc_pos+read_length;
1593 info->read_pos+=length;
1594 }
1595 else
1596 next_pos_in_file=(info->pos_in_file+ (size_t)
1597 (info->read_end - info->request_pos));
1598
1599 /* If reading large blocks, or first read or read with skip */
1600 if (Count)
1601 {
1602 if (next_pos_in_file == info->end_of_file)
1603 {
1604 info->error=(int) (read_length+left_length);
1605 return 1;
1606 }
1607
1608 if (mysql_file_seek(info->file, next_pos_in_file, MY_SEEK_SET, MYF(0))
1609 == MY_FILEPOS_ERROR)
1610 {
1611 info->error= -1;
1612 return (1);
1613 }
1614
1615 read_length=IO_SIZE*2- (size_t) (next_pos_in_file & (IO_SIZE-1));
1616 if (Count < read_length)
1617 { /* Small block, read to cache */
1618 if ((read_length=mysql_file_read(info->file,info->request_pos,
1619 read_length, info->myflags)) == (size_t) -1)
1620 return info->error= -1;
1621 use_length=MY_MIN(Count,read_length);
1622 memcpy(Buffer,info->request_pos,(size_t) use_length);
1623 info->read_pos=info->request_pos+Count;
1624 info->read_end=info->request_pos+read_length;
1625 info->pos_in_file=next_pos_in_file; /* Start of block in cache */
1626 next_pos_in_file+=read_length;
1627
1628 if (Count != use_length)
1629 { /* Didn't find hole block */
1630 if (info->myflags & (MY_WME | MY_FAE | MY_FNABP) && Count != org_Count)
1631 my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG),
1632 my_filename(info->file),my_errno);
1633 info->error=(int) (read_length+left_length);
1634 return 1;
1635 }
1636 }
1637 else
1638 { /* Big block, don't cache it */
1639 if ((read_length= mysql_file_read(info->file, Buffer, Count,info->myflags))
1640 != Count)
1641 {
1642 info->error= read_length == (size_t) -1 ? -1 : read_length+left_length;
1643 return 1;
1644 }
1645 info->read_pos=info->read_end=info->request_pos;
1646 info->pos_in_file=(next_pos_in_file+=Count);
1647 }
1648 }
1649
1650 /* Read next block with asyncronic io */
1651 diff_length=(next_pos_in_file & (IO_SIZE-1));
1652 max_length= info->read_length - diff_length;
1653 if (max_length > info->end_of_file - next_pos_in_file)
1654 max_length= (size_t) (info->end_of_file - next_pos_in_file);
1655
1656 if (info->request_pos != info->buffer)
1657 read_buffer=info->buffer;
1658 else
1659 read_buffer=info->buffer+info->read_length;
1660 info->aio_read_pos=next_pos_in_file;
1661 if (max_length)
1662 {
1663 info->aio_result.result.aio_errno=AIO_INPROGRESS; /* Marker for test */
1664 DBUG_PRINT("aioread",("filepos: %ld length: %lu",
1665 (ulong) next_pos_in_file, (ulong) max_length));
1666 if (aioread(info->file,read_buffer, max_length,
1667 (my_off_t) next_pos_in_file,MY_SEEK_SET,
1668 &info->aio_result.result))
1669 { /* Skip async io */
1670 my_errno=errno;
1671 DBUG_PRINT("error",("got error: %d, aio_result: %d from aioread, async skipped",
1672 errno, info->aio_result.result.aio_errno));
1673 if (info->request_pos != info->buffer)
1674 {
1675 bmove(info->buffer,info->request_pos,
1676 (size_t) (info->read_end - info->read_pos));
1677 info->request_pos=info->buffer;
1678 info->read_pos-=info->read_length;
1679 info->read_end-=info->read_length;
1680 }
1681 info->read_length=info->buffer_length; /* Use hole buffer */
1682 info->read_function=_my_b_cache_read; /* Use normal IO_READ next */
1683 }
1684 else
1685 info->inited=info->aio_result.pending=1;
1686 }
1687 return 0; /* Block read, async in use */
1688} /* _my_b_async_read */
1689#endif
1690
1691
1692/* Read one byte when buffer is empty */
1693
1694int _my_b_get(IO_CACHE *info)
1695{
1696 uchar buff;
1697 if ((*(info)->read_function)(info,&buff,1))
1698 return my_b_EOF;
1699 return (int) (uchar) buff;
1700}
1701
1702/*
1703 Write a byte buffer to IO_CACHE and flush to disk
1704 if IO_CACHE is full.
1705
1706 RETURN VALUE
1707 1 On error on write
1708 0 On success
1709 -1 On error; my_errno contains error code.
1710*/
1711
1712int _my_b_cache_write(IO_CACHE *info, const uchar *Buffer, size_t Count)
1713{
1714 if (Buffer != info->write_buffer)
1715 {
1716 Count= IO_ROUND_DN(Count);
1717 if (!Count)
1718 return 0;
1719 }
1720
1721 if (info->seek_not_done)
1722 {
1723 /*
1724 Whenever a function which operates on IO_CACHE flushes/writes
1725 some part of the IO_CACHE to disk it will set the property
1726 "seek_not_done" to indicate this to other functions operating
1727 on the IO_CACHE.
1728 */
1729 if (mysql_file_seek(info->file, info->pos_in_file, MY_SEEK_SET,
1730 MYF(info->myflags & MY_WME)) == MY_FILEPOS_ERROR)
1731 {
1732 info->error= -1;
1733 return 1;
1734 }
1735 info->seek_not_done=0;
1736 }
1737 if (mysql_file_write(info->file, Buffer, Count, info->myflags | MY_NABP))
1738 return info->error= -1;
1739
1740 info->pos_in_file+= Count;
1741 return 0;
1742}
1743
1744
1745/*
1746 In case of a shared I/O cache with a writer we normally do direct
1747 write cache to read cache copy. Simulate this here by direct
1748 caller buffer to read cache copy. Do it after the write so that
1749 the cache readers actions on the flushed part can go in parallel
1750 with the write of the extra stuff. copy_to_read_buffer()
1751 synchronizes writer and readers so that after this call the
1752 readers can act on the extra stuff while the writer can go ahead
1753 and prepare the next output. copy_to_read_buffer() relies on
1754 info->pos_in_file.
1755*/
1756static int _my_b_cache_write_r(IO_CACHE *info, const uchar *Buffer, size_t Count)
1757{
1758 my_off_t old_pos_in_file= info->pos_in_file;
1759 int res= _my_b_cache_write(info, Buffer, Count);
1760 if (res)
1761 return res;
1762
1763 DBUG_ASSERT(!(info->myflags & MY_ENCRYPT));
1764 DBUG_ASSERT(info->share);
1765 copy_to_read_buffer(info, Buffer, old_pos_in_file);
1766
1767 return 0;
1768}
1769
1770
1771/*
1772 Append a block to the write buffer.
1773 This is done with the buffer locked to ensure that we don't read from
1774 the write buffer before we are ready with it.
1775*/
1776
1777int my_b_append(IO_CACHE *info, const uchar *Buffer, size_t Count)
1778{
1779 size_t rest_length,length;
1780
1781 /*
1782 Assert that we cannot come here with a shared cache. If we do one
1783 day, we might need to add a call to copy_to_read_buffer().
1784 */
1785 DBUG_ASSERT(!info->share);
1786 DBUG_ASSERT(!(info->myflags & MY_ENCRYPT));
1787
1788 lock_append_buffer(info);
1789 rest_length= (size_t) (info->write_end - info->write_pos);
1790 if (Count <= rest_length)
1791 goto end;
1792 memcpy(info->write_pos, Buffer, rest_length);
1793 Buffer+=rest_length;
1794 Count-=rest_length;
1795 info->write_pos+=rest_length;
1796 if (my_b_flush_io_cache(info,0))
1797 {
1798 unlock_append_buffer(info);
1799 return 1;
1800 }
1801 if (Count >= IO_SIZE)
1802 { /* Fill first intern buffer */
1803 length= IO_ROUND_DN(Count);
1804 if (mysql_file_write(info->file,Buffer, length, info->myflags | MY_NABP))
1805 {
1806 unlock_append_buffer(info);
1807 return info->error= -1;
1808 }
1809 Count-=length;
1810 Buffer+=length;
1811 info->end_of_file+=length;
1812 }
1813
1814end:
1815 memcpy(info->write_pos,Buffer,(size_t) Count);
1816 info->write_pos+=Count;
1817 unlock_append_buffer(info);
1818 return 0;
1819}
1820
1821
1822int my_b_safe_write(IO_CACHE *info, const uchar *Buffer, size_t Count)
1823{
1824 /*
1825 Sasha: We are not writing this with the ? operator to avoid hitting
1826 a possible compiler bug. At least gcc 2.95 cannot deal with
1827 several layers of ternary operators that evaluated comma(,) operator
1828 expressions inside - I do have a test case if somebody wants it
1829 */
1830 if (info->type == SEQ_READ_APPEND)
1831 return my_b_append(info, Buffer, Count);
1832 return my_b_write(info, Buffer, Count);
1833}
1834
1835
1836/*
1837 Write a block to disk where part of the data may be inside the record
1838 buffer. As all write calls to the data goes through the cache,
1839 we will never get a seek over the end of the buffer
1840*/
1841
1842int my_block_write(IO_CACHE *info, const uchar *Buffer, size_t Count,
1843 my_off_t pos)
1844{
1845 size_t length;
1846 int error=0;
1847
1848 /*
1849 Assert that we cannot come here with a shared cache. If we do one
1850 day, we might need to add a call to copy_to_read_buffer().
1851 */
1852 DBUG_ASSERT(!info->share);
1853 DBUG_ASSERT(!(info->myflags & MY_ENCRYPT));
1854
1855 if (pos < info->pos_in_file)
1856 {
1857 /* Of no overlap, write everything without buffering */
1858 if (pos + Count <= info->pos_in_file)
1859 return (int)mysql_file_pwrite(info->file, Buffer, Count, pos,
1860 info->myflags | MY_NABP);
1861 /* Write the part of the block that is before buffer */
1862 length= (uint) (info->pos_in_file - pos);
1863 if (mysql_file_pwrite(info->file, Buffer, length, pos, info->myflags | MY_NABP))
1864 info->error= error= -1;
1865 Buffer+=length;
1866 pos+= length;
1867 Count-= length;
1868 }
1869
1870 /* Check if we want to write inside the used part of the buffer.*/
1871 length= (size_t) (info->write_end - info->buffer);
1872 if (pos < info->pos_in_file + length)
1873 {
1874 size_t offset= (size_t) (pos - info->pos_in_file);
1875 length-=offset;
1876 if (length > Count)
1877 length=Count;
1878 memcpy(info->buffer+offset, Buffer, length);
1879 Buffer+=length;
1880 Count-= length;
1881 /* Fix length of buffer if the new data was larger */
1882 if (info->buffer+length > info->write_pos)
1883 info->write_pos=info->buffer+length;
1884 if (!Count)
1885 return (error);
1886 }
1887 /* Write at the end of the current buffer; This is the normal case */
1888 if (_my_b_write(info, Buffer, Count))
1889 error= -1;
1890 return error;
1891}
1892
1893
1894 /* Flush write cache */
1895
1896#define LOCK_APPEND_BUFFER if (need_append_buffer_lock) \
1897 lock_append_buffer(info);
1898#define UNLOCK_APPEND_BUFFER if (need_append_buffer_lock) \
1899 unlock_append_buffer(info);
1900
1901int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock)
1902{
1903 size_t length;
1904 my_bool append_cache= (info->type == SEQ_READ_APPEND);
1905 DBUG_ENTER("my_b_flush_io_cache");
1906 DBUG_PRINT("enter", ("cache: %p", info));
1907
1908 if (!append_cache)
1909 need_append_buffer_lock= 0;
1910
1911 if (info->type == WRITE_CACHE || append_cache)
1912 {
1913 if (info->file == -1)
1914 {
1915 if (real_open_cached_file(info))
1916 DBUG_RETURN((info->error= -1));
1917 }
1918 LOCK_APPEND_BUFFER;
1919
1920 if ((length=(size_t) (info->write_pos - info->write_buffer)))
1921 {
1922 if (append_cache)
1923 {
1924 if (mysql_file_write(info->file, info->write_buffer, length,
1925 info->myflags | MY_NABP))
1926 {
1927 info->error= -1;
1928 DBUG_RETURN(-1);
1929 }
1930 info->end_of_file+= info->write_pos - info->append_read_pos;
1931 info->append_read_pos= info->write_buffer;
1932 DBUG_ASSERT(info->end_of_file == mysql_file_tell(info->file, MYF(0)));
1933 }
1934 else
1935 {
1936 int res= info->write_function(info, info->write_buffer, length);
1937 if (res)
1938 DBUG_RETURN(res);
1939
1940 set_if_bigger(info->end_of_file, info->pos_in_file);
1941 }
1942 info->write_end= (info->write_buffer + info->buffer_length -
1943 ((info->pos_in_file + length) & (IO_SIZE - 1)));
1944 info->write_pos= info->write_buffer;
1945 ++info->disk_writes;
1946 UNLOCK_APPEND_BUFFER;
1947 DBUG_RETURN(info->error);
1948 }
1949 }
1950#ifdef HAVE_AIOWAIT
1951 else if (info->type != READ_NET)
1952 {
1953 my_aiowait(&info->aio_result); /* Wait for outstanding req */
1954 info->inited=0;
1955 }
1956#endif
1957 UNLOCK_APPEND_BUFFER;
1958 DBUG_RETURN(0);
1959}
1960
1961/*
1962 Free an IO_CACHE object
1963
1964 SYNOPSOS
1965 end_io_cache()
1966 info IO_CACHE Handle to free
1967
1968 NOTES
1969 It's currently safe to call this if one has called init_io_cache()
1970 on the 'info' object, even if init_io_cache() failed.
1971 This function is also safe to call twice with the same handle.
1972 Note that info->file is not reset as the caller may still use ut for my_close()
1973
1974 RETURN
1975 0 ok
1976 # Error
1977*/
1978
1979int end_io_cache(IO_CACHE *info)
1980{
1981 int error=0;
1982 DBUG_ENTER("end_io_cache");
1983 DBUG_PRINT("enter",("cache: %p", info));
1984
1985 /*
1986 Every thread must call remove_io_thread(). The last one destroys
1987 the share elements.
1988 */
1989 DBUG_ASSERT(!info->share || !info->share->total_threads);
1990
1991 if (info->alloced_buffer)
1992 {
1993 info->alloced_buffer=0;
1994 if (info->file != -1) /* File doesn't exist */
1995 error= my_b_flush_io_cache(info,1);
1996 my_free(info->buffer);
1997 info->buffer=info->read_pos=(uchar*) 0;
1998 }
1999 if (info->type == SEQ_READ_APPEND)
2000 {
2001 /* Destroy allocated mutex */
2002 mysql_mutex_destroy(&info->append_buffer_lock);
2003 }
2004 info->share= 0;
2005 info->type= TYPE_NOT_SET; /* Ensure that flush_io_cache() does nothing */
2006 info->write_end= 0; /* Ensure that my_b_write() fails */
2007 info->write_function= 0; /* my_b_write will crash if used */
2008 DBUG_RETURN(error);
2009} /* end_io_cache */
2010
2011
2012/**********************************************************************
2013 Testing of MF_IOCACHE
2014**********************************************************************/
2015
2016#ifdef MAIN
2017
2018#include <my_dir.h>
2019
2020void die(const char* fmt, ...)
2021{
2022 va_list va_args;
2023 va_start(va_args,fmt);
2024 fprintf(stderr,"Error:");
2025 vfprintf(stderr, fmt,va_args);
2026 fprintf(stderr,", errno=%d\n", errno);
2027 va_end(va_args);
2028 exit(1);
2029}
2030
2031int open_file(const char* fname, IO_CACHE* info, int cache_size)
2032{
2033 int fd;
2034 if ((fd=my_open(fname,O_CREAT | O_RDWR,MYF(MY_WME))) < 0)
2035 die("Could not open %s", fname);
2036 if (init_io_cache(info, fd, cache_size, SEQ_READ_APPEND, 0,0,MYF(MY_WME)))
2037 die("failed in init_io_cache()");
2038 return fd;
2039}
2040
2041void close_file(IO_CACHE* info)
2042{
2043 end_io_cache(info);
2044 my_close(info->file, MYF(MY_WME));
2045}
2046
2047int main(int argc, char** argv)
2048{
2049 IO_CACHE sra_cache; /* SEQ_READ_APPEND */
2050 MY_STAT status;
2051 const char* fname="/tmp/iocache.test";
2052 int cache_size=16384;
2053 char llstr_buf[22];
2054 int max_block,total_bytes=0;
2055 int i,num_loops=100,error=0;
2056 char *p;
2057 char* block, *block_end;
2058 MY_INIT(argv[0]);
2059 max_block = cache_size*3;
2060 if (!(block=(char*)my_malloc(max_block,MYF(MY_WME))))
2061 die("Not enough memory to allocate test block");
2062 block_end = block + max_block;
2063 for (p = block,i=0; p < block_end;i++)
2064 {
2065 *p++ = (char)i;
2066 }
2067 if (my_stat(fname,&status, MYF(0)) &&
2068 my_delete(fname,MYF(MY_WME)))
2069 {
2070 die("Delete of %s failed, aborting", fname);
2071 }
2072 open_file(fname,&sra_cache, cache_size);
2073 for (i = 0; i < num_loops; i++)
2074 {
2075 char buf[4];
2076 int block_size = abs(rand() % max_block);
2077 int4store(buf, block_size);
2078 if (my_b_append(&sra_cache,buf,4) ||
2079 my_b_append(&sra_cache, block, block_size))
2080 die("write failed");
2081 total_bytes += 4+block_size;
2082 }
2083 close_file(&sra_cache);
2084 my_free(block);
2085 if (!my_stat(fname,&status,MYF(MY_WME)))
2086 die("%s failed to stat, but I had just closed it,\
2087 wonder how that happened");
2088 printf("Final size of %s is %s, wrote %d bytes\n",fname,
2089 llstr(status.st_size,llstr_buf),
2090 total_bytes);
2091 my_delete(fname, MYF(MY_WME));
2092 /* check correctness of tests */
2093 if (total_bytes != status.st_size)
2094 {
2095 fprintf(stderr,"Not the same number of bytes actually in file as bytes \
2096supposedly written\n");
2097 error=1;
2098 }
2099 exit(error);
2100 return 0;
2101}
2102#endif
2103