00001 /* Copyright (C) 2000 MySQL AB 00002 00003 This program is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU General Public License as published by 00005 the Free Software Foundation; either version 2 of the License, or 00006 (at your option) any later version. 00007 00008 This program is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00011 GNU General Public License for more details. 00012 00013 You should have received a copy of the GNU General Public License 00014 along with this program; if not, write to the Free Software 00015 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ 00016 00017 /* 00018 Cashing of files with only does (sequential) read or writes of fixed- 00019 length records. A read isn't allowed to go over file-length. A read is ok 00020 if it ends at file-length and next read can try to read after file-length 00021 (and get a EOF-error). 00022 Possibly use of asyncronic io. 00023 macros for read and writes for faster io. 00024 Used instead of FILE when reading or writing whole files. 00025 This code makes mf_rec_cache obsolete (currently only used by ISAM) 00026 One can change info->pos_in_file to a higher value to skip bytes in file if 00027 also info->read_pos is set to info->read_end. 00028 If called through open_cached_file(), then the temporary file will 00029 only be created if a write exeeds the file buffer or if one calls 00030 my_b_flush_io_cache(). 00031 00032 If one uses SEQ_READ_APPEND, then two buffers are allocated, one for 00033 reading and another for writing. Reads are first done from disk and 00034 then done from the write buffer. This is an efficient way to read 00035 from a log file when one is writing to it at the same time. 00036 For this to work, the file has to be opened in append mode! 00037 Note that when one uses SEQ_READ_APPEND, one MUST write using 00038 my_b_append ! This is needed because we need to lock the mutex 00039 every time we access the write buffer. 00040 00041 TODO: 00042 When one SEQ_READ_APPEND and we are reading and writing at the same time, 00043 each time the write buffer gets full and it's written to disk, we will 00044 always do a disk read to read a part of the buffer from disk to the 00045 read buffer. 00046 This should be fixed so that when we do a my_b_flush_io_cache() and 00047 we have been reading the write buffer, we should transfer the rest of the 00048 write buffer to the read buffer before we start to reuse it. 00049 */ 00050 00051 #define MAP_TO_USE_RAID 00052 #include "mysys_priv.h" 00053 #include <m_string.h> 00054 #ifdef HAVE_AIOWAIT 00055 #include "mysys_err.h" 00056 static void my_aiowait(my_aio_result *result); 00057 #endif 00058 #include <errno.h> 00059 00060 #ifdef THREAD 00061 #define lock_append_buffer(info) \ 00062 pthread_mutex_lock(&(info)->append_buffer_lock) 00063 #define unlock_append_buffer(info) \ 00064 pthread_mutex_unlock(&(info)->append_buffer_lock) 00065 #else 00066 #define lock_append_buffer(info) 00067 #define unlock_append_buffer(info) 00068 #endif 00069 00070 #define IO_ROUND_UP(X) (((X)+IO_SIZE-1) & ~(IO_SIZE-1)) 00071 #define IO_ROUND_DN(X) ( (X) & ~(IO_SIZE-1)) 00072 00073 00074 /* 00075 Setup internal pointers inside IO_CACHE 00076 00077 SYNOPSIS 00078 setup_io_cache() 00079 info IO_CACHE handler 00080 00081 NOTES 00082 This is called on automaticly on init or reinit of IO_CACHE 00083 It must be called externally if one moves or copies an IO_CACHE 00084 object. 00085 */ 00086 00087 void setup_io_cache(IO_CACHE* info) 00088 { 00089 /* Ensure that my_b_tell() and my_b_bytes_in_cache works */ 00090 if (info->type == WRITE_CACHE) 00091 { 00092 info->current_pos= &info->write_pos; 00093 info->current_end= &info->write_end; 00094 } 00095 else 00096 { 00097 info->current_pos= &info->read_pos; 00098 info->current_end= &info->read_end; 00099 } 00100 } 00101 00102 00103 static void 00104 init_functions(IO_CACHE* info) 00105 { 00106 enum cache_type type= info->type; 00107 switch (type) { 00108 case READ_NET: 00109 /* 00110 Must be initialized by the caller. The problem is that 00111 _my_b_net_read has to be defined in sql directory because of 00112 the dependency on THD, and therefore cannot be visible to 00113 programs that link against mysys but know nothing about THD, such 00114 as myisamchk 00115 */ 00116 break; 00117 case SEQ_READ_APPEND: 00118 info->read_function = _my_b_seq_read; 00119 info->write_function = 0; /* Force a core if used */ 00120 break; 00121 default: 00122 info->read_function = 00123 #ifdef THREAD 00124 info->share ? _my_b_read_r : 00125 #endif 00126 _my_b_read; 00127 info->write_function = _my_b_write; 00128 } 00129 00130 setup_io_cache(info); 00131 } 00132 00133 00134 /* 00135 Initialize an IO_CACHE object 00136 00137 SYNOPSOS 00138 init_io_cache() 00139 info cache handler to initialize 00140 file File that should be associated to to the handler 00141 If == -1 then real_open_cached_file() 00142 will be called when it's time to open file. 00143 cachesize Size of buffer to allocate for read/write 00144 If == 0 then use my_default_record_cache_size 00145 type Type of cache 00146 seek_offset Where cache should start reading/writing 00147 use_async_io Set to 1 of we should use async_io (if avaiable) 00148 cache_myflags Bitmap of differnt flags 00149 MY_WME | MY_FAE | MY_NABP | MY_FNABP | 00150 MY_DONT_CHECK_FILESIZE 00151 00152 RETURN 00153 0 ok 00154 # error 00155 */ 00156 00157 int init_io_cache(IO_CACHE *info, File file, uint cachesize, 00158 enum cache_type type, my_off_t seek_offset, 00159 pbool use_async_io, myf cache_myflags) 00160 { 00161 uint min_cache; 00162 my_off_t end_of_file= ~(my_off_t) 0; 00163 DBUG_ENTER("init_io_cache"); 00164 DBUG_PRINT("enter",("cache: 0x%lx type: %d pos: %ld", 00165 (ulong) info, (int) type, (ulong) seek_offset)); 00166 00167 info->file= file; 00168 info->type= TYPE_NOT_SET; /* Don't set it until mutex are created */ 00169 info->pos_in_file= seek_offset; 00170 info->pre_close = info->pre_read = info->post_read = 0; 00171 info->arg = 0; 00172 info->alloced_buffer = 0; 00173 info->buffer=0; 00174 info->seek_not_done= test(file >= 0 && seek_offset != my_tell(file, MYF(0))); 00175 info->disk_writes= 0; 00176 #ifdef THREAD 00177 info->share=0; 00178 #endif 00179 00180 if (!cachesize && !(cachesize= my_default_record_cache_size)) 00181 DBUG_RETURN(1); /* No cache requested */ 00182 min_cache=use_async_io ? IO_SIZE*4 : IO_SIZE*2; 00183 if (type == READ_CACHE || type == SEQ_READ_APPEND) 00184 { /* Assume file isn't growing */ 00185 if (!(cache_myflags & MY_DONT_CHECK_FILESIZE)) 00186 { 00187 /* Calculate end of file to avoid allocating oversized buffers */ 00188 end_of_file=my_seek(file,0L,MY_SEEK_END,MYF(0)); 00189 /* Need to reset seek_not_done now that we just did a seek. */ 00190 info->seek_not_done= end_of_file == seek_offset ? 0 : 1; 00191 if (end_of_file < seek_offset) 00192 end_of_file=seek_offset; 00193 /* Trim cache size if the file is very small */ 00194 if ((my_off_t) cachesize > end_of_file-seek_offset+IO_SIZE*2-1) 00195 { 00196 cachesize=(uint) (end_of_file-seek_offset)+IO_SIZE*2-1; 00197 use_async_io=0; /* No need to use async */ 00198 } 00199 } 00200 } 00201 cache_myflags &= ~MY_DONT_CHECK_FILESIZE; 00202 if (type != READ_NET && type != WRITE_NET) 00203 { 00204 /* Retry allocating memory in smaller blocks until we get one */ 00205 for (;;) 00206 { 00207 uint buffer_block; 00208 cachesize=(uint) ((ulong) (cachesize + min_cache-1) & 00209 (ulong) ~(min_cache-1)); 00210 if (cachesize < min_cache) 00211 cachesize = min_cache; 00212 buffer_block = cachesize; 00213 if (type == SEQ_READ_APPEND) 00214 buffer_block *= 2; 00215 if ((info->buffer= 00216 (byte*) my_malloc(buffer_block, 00217 MYF((cache_myflags & ~ MY_WME) | 00218 (cachesize == min_cache ? MY_WME : 0)))) != 0) 00219 { 00220 info->write_buffer=info->buffer; 00221 if (type == SEQ_READ_APPEND) 00222 info->write_buffer = info->buffer + cachesize; 00223 info->alloced_buffer=1; 00224 break; /* Enough memory found */ 00225 } 00226 if (cachesize == min_cache) 00227 DBUG_RETURN(2); /* Can't alloc cache */ 00228 cachesize= (uint) ((long) cachesize*3/4); /* Try with less memory */ 00229 } 00230 } 00231 00232 DBUG_PRINT("info",("init_io_cache: cachesize = %u",cachesize)); 00233 info->read_length=info->buffer_length=cachesize; 00234 info->myflags=cache_myflags & ~(MY_NABP | MY_FNABP); 00235 info->request_pos= info->read_pos= info->write_pos = info->buffer; 00236 if (type == SEQ_READ_APPEND) 00237 { 00238 info->append_read_pos = info->write_pos = info->write_buffer; 00239 info->write_end = info->write_buffer + info->buffer_length; 00240 #ifdef THREAD 00241 pthread_mutex_init(&info->append_buffer_lock,MY_MUTEX_INIT_FAST); 00242 #endif 00243 } 00244 #if defined(SAFE_MUTEX) && defined(THREAD) 00245 else 00246 { 00247 /* Clear mutex so that safe_mutex will notice that it's not initialized */ 00248 bzero((char*) &info->append_buffer_lock, sizeof(info)); 00249 } 00250 #endif 00251 00252 if (type == WRITE_CACHE) 00253 info->write_end= 00254 info->buffer+info->buffer_length- (seek_offset & (IO_SIZE-1)); 00255 else 00256 info->read_end=info->buffer; /* Nothing in cache */ 00257 00258 /* End_of_file may be changed by user later */ 00259 info->end_of_file= end_of_file; 00260 info->error=0; 00261 info->type= type; 00262 init_functions(info); 00263 #ifdef HAVE_AIOWAIT 00264 if (use_async_io && ! my_disable_async_io) 00265 { 00266 DBUG_PRINT("info",("Using async io")); 00267 info->read_length/=2; 00268 info->read_function=_my_b_async_read; 00269 } 00270 info->inited=info->aio_result.pending=0; 00271 #endif 00272 DBUG_RETURN(0); 00273 } /* init_io_cache */ 00274 00275 /* Wait until current request is ready */ 00276 00277 #ifdef HAVE_AIOWAIT 00278 static void my_aiowait(my_aio_result *result) 00279 { 00280 if (result->pending) 00281 { 00282 struct aio_result_t *tmp; 00283 for (;;) 00284 { 00285 if ((int) (tmp=aiowait((struct timeval *) 0)) == -1) 00286 { 00287 if (errno == EINTR) 00288 continue; 00289 DBUG_PRINT("error",("No aio request, error: %d",errno)); 00290 result->pending=0; /* Assume everythings is ok */ 00291 break; 00292 } 00293 ((my_aio_result*) tmp)->pending=0; 00294 if ((my_aio_result*) tmp == result) 00295 break; 00296 } 00297 } 00298 return; 00299 } 00300 #endif 00301 00302 00303 /* 00304 Use this to reset cache to re-start reading or to change the type 00305 between READ_CACHE <-> WRITE_CACHE 00306 If we are doing a reinit of a cache where we have the start of the file 00307 in the cache, we are reusing this memory without flushing it to disk. 00308 */ 00309 00310 my_bool reinit_io_cache(IO_CACHE *info, enum cache_type type, 00311 my_off_t seek_offset, 00312 pbool use_async_io __attribute__((unused)), 00313 pbool clear_cache) 00314 { 00315 DBUG_ENTER("reinit_io_cache"); 00316 DBUG_PRINT("enter",("cache: 0x%lx type: %d seek_offset: %lu clear_cache: %d", 00317 (ulong) info, type, (ulong) seek_offset, 00318 (int) clear_cache)); 00319 00320 /* One can't do reinit with the following types */ 00321 DBUG_ASSERT(type != READ_NET && info->type != READ_NET && 00322 type != WRITE_NET && info->type != WRITE_NET && 00323 type != SEQ_READ_APPEND && info->type != SEQ_READ_APPEND); 00324 00325 /* If the whole file is in memory, avoid flushing to disk */ 00326 if (! clear_cache && 00327 seek_offset >= info->pos_in_file && 00328 seek_offset <= my_b_tell(info)) 00329 { 00330 /* Reuse current buffer without flushing it to disk */ 00331 byte *pos; 00332 if (info->type == WRITE_CACHE && type == READ_CACHE) 00333 { 00334 info->read_end=info->write_pos; 00335 info->end_of_file=my_b_tell(info); 00336 info->seek_not_done=1; 00337 } 00338 else if (type == WRITE_CACHE) 00339 { 00340 if (info->type == READ_CACHE) 00341 { 00342 info->write_end=info->write_buffer+info->buffer_length; 00343 info->seek_not_done=1; 00344 } 00345 info->end_of_file = ~(my_off_t) 0; 00346 } 00347 pos=info->request_pos+(seek_offset-info->pos_in_file); 00348 if (type == WRITE_CACHE) 00349 info->write_pos=pos; 00350 else 00351 info->read_pos= pos; 00352 #ifdef HAVE_AIOWAIT 00353 my_aiowait(&info->aio_result); /* Wait for outstanding req */ 00354 #endif 00355 } 00356 else 00357 { 00358 /* 00359 If we change from WRITE_CACHE to READ_CACHE, assume that everything 00360 after the current positions should be ignored 00361 */ 00362 if (info->type == WRITE_CACHE && type == READ_CACHE) 00363 info->end_of_file=my_b_tell(info); 00364 /* flush cache if we want to reuse it */ 00365 if (!clear_cache && my_b_flush_io_cache(info,1)) 00366 DBUG_RETURN(1); 00367 info->pos_in_file=seek_offset; 00368 /* Better to do always do a seek */ 00369 info->seek_not_done=1; 00370 info->request_pos=info->read_pos=info->write_pos=info->buffer; 00371 if (type == READ_CACHE) 00372 { 00373 info->read_end=info->buffer; /* Nothing in cache */ 00374 } 00375 else 00376 { 00377 info->write_end=(info->buffer + info->buffer_length - 00378 (seek_offset & (IO_SIZE-1))); 00379 info->end_of_file= ~(my_off_t) 0; 00380 } 00381 } 00382 info->type=type; 00383 info->error=0; 00384 init_functions(info); 00385 00386 #ifdef HAVE_AIOWAIT 00387 if (use_async_io && ! my_disable_async_io && 00388 ((ulong) info->buffer_length < 00389 (ulong) (info->end_of_file - seek_offset))) 00390 { 00391 info->read_length=info->buffer_length/2; 00392 info->read_function=_my_b_async_read; 00393 } 00394 info->inited=0; 00395 #endif 00396 DBUG_RETURN(0); 00397 } /* reinit_io_cache */ 00398 00399 00400 00401 /* 00402 Read buffered. 00403 00404 SYNOPSIS 00405 _my_b_read() 00406 info IO_CACHE pointer 00407 Buffer Buffer to retrieve count bytes from file 00408 Count Number of bytes to read into Buffer 00409 00410 NOTE 00411 This function is only called from the my_b_read() macro when there 00412 isn't enough characters in the buffer to satisfy the request. 00413 00414 WARNING 00415 00416 When changing this function, be careful with handling file offsets 00417 (end-of_file, pos_in_file). Do not cast them to possibly smaller 00418 types than my_off_t unless you can be sure that their value fits. 00419 Same applies to differences of file offsets. 00420 00421 When changing this function, check _my_b_read_r(). It might need the 00422 same change. 00423 00424 RETURN 00425 0 we succeeded in reading all data 00426 1 Error: can't read requested characters 00427 */ 00428 00429 int _my_b_read(register IO_CACHE *info, byte *Buffer, uint Count) 00430 { 00431 uint length,diff_length,left_length; 00432 my_off_t max_length, pos_in_file; 00433 DBUG_ENTER("_my_b_read"); 00434 00435 if ((left_length=(uint) (info->read_end-info->read_pos))) 00436 { 00437 DBUG_ASSERT(Count >= left_length); /* User is not using my_b_read() */ 00438 memcpy(Buffer,info->read_pos, (size_t) (left_length)); 00439 Buffer+=left_length; 00440 Count-=left_length; 00441 } 00442 00443 /* pos_in_file always point on where info->buffer was read */ 00444 pos_in_file=info->pos_in_file+(uint) (info->read_end - info->buffer); 00445 if (info->seek_not_done) 00446 { /* File touched, do seek */ 00447 VOID(my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0))); 00448 info->seek_not_done=0; 00449 } 00450 diff_length=(uint) (pos_in_file & (IO_SIZE-1)); 00451 if (Count >= (uint) (IO_SIZE+(IO_SIZE-diff_length))) 00452 { /* Fill first intern buffer */ 00453 uint read_length; 00454 if (info->end_of_file <= pos_in_file) 00455 { /* End of file */ 00456 info->error=(int) left_length; 00457 DBUG_RETURN(1); 00458 } 00459 length=(Count & (uint) ~(IO_SIZE-1))-diff_length; 00460 if ((read_length=my_read(info->file,Buffer,(uint) length,info->myflags)) 00461 != (uint) length) 00462 { 00463 info->error= (read_length == (uint) -1 ? -1 : 00464 (int) (read_length+left_length)); 00465 DBUG_RETURN(1); 00466 } 00467 Count-=length; 00468 Buffer+=length; 00469 pos_in_file+=length; 00470 left_length+=length; 00471 diff_length=0; 00472 } 00473 00474 max_length=info->read_length-diff_length; 00475 if (info->type != READ_FIFO && 00476 max_length > (info->end_of_file - pos_in_file)) 00477 max_length = info->end_of_file - pos_in_file; 00478 if (!max_length) 00479 { 00480 if (Count) 00481 { 00482 info->error= left_length; /* We only got this many char */ 00483 DBUG_RETURN(1); 00484 } 00485 length=0; /* Didn't read any chars */ 00486 } 00487 else if ((length=my_read(info->file,info->buffer,(uint) max_length, 00488 info->myflags)) < Count || 00489 length == (uint) -1) 00490 { 00491 if (length != (uint) -1) 00492 memcpy(Buffer,info->buffer,(size_t) length); 00493 info->pos_in_file= pos_in_file; 00494 info->error= length == (uint) -1 ? -1 : (int) (length+left_length); 00495 info->read_pos=info->read_end=info->buffer; 00496 DBUG_RETURN(1); 00497 } 00498 info->read_pos=info->buffer+Count; 00499 info->read_end=info->buffer+length; 00500 info->pos_in_file=pos_in_file; 00501 memcpy(Buffer,info->buffer,(size_t) Count); 00502 DBUG_RETURN(0); 00503 } 00504 00505 #ifdef THREAD 00506 /* Prepare IO_CACHE for shared use */ 00507 void init_io_cache_share(IO_CACHE *info, IO_CACHE_SHARE *s, uint num_threads) 00508 { 00509 DBUG_ASSERT(info->type == READ_CACHE); 00510 pthread_mutex_init(&s->mutex, MY_MUTEX_INIT_FAST); 00511 pthread_cond_init (&s->cond, 0); 00512 s->total=s->count=num_threads-1; 00513 s->active=0; 00514 info->share=s; 00515 info->read_function=_my_b_read_r; 00516 info->current_pos= info->current_end= 0; 00517 } 00518 00519 /* 00520 Remove a thread from shared access to IO_CACHE 00521 Every thread should do that on exit for not 00522 to deadlock other threads 00523 */ 00524 void remove_io_thread(IO_CACHE *info) 00525 { 00526 IO_CACHE_SHARE *s=info->share; 00527 00528 pthread_mutex_lock(&s->mutex); 00529 s->total--; 00530 if (! s->count--) 00531 pthread_cond_signal(&s->cond); 00532 pthread_mutex_unlock(&s->mutex); 00533 } 00534 00535 static int lock_io_cache(IO_CACHE *info, my_off_t pos) 00536 { 00537 int total; 00538 IO_CACHE_SHARE *s=info->share; 00539 00540 pthread_mutex_lock(&s->mutex); 00541 if (!s->count) 00542 { 00543 s->count=s->total; 00544 return 1; 00545 } 00546 00547 total=s->total; 00548 s->count--; 00549 while (!s->active || s->active->pos_in_file < pos) 00550 pthread_cond_wait(&s->cond, &s->mutex); 00551 00552 if (s->total < total && 00553 (!s->active || s->active->pos_in_file < pos)) 00554 return 1; 00555 00556 pthread_mutex_unlock(&s->mutex); 00557 return 0; 00558 } 00559 00560 static void unlock_io_cache(IO_CACHE *info) 00561 { 00562 pthread_cond_broadcast(&info->share->cond); 00563 pthread_mutex_unlock(&info->share->mutex); 00564 } 00565 00566 00567 /* 00568 Read from IO_CACHE when it is shared between several threads. 00569 00570 SYNOPSIS 00571 _my_b_read_r() 00572 info IO_CACHE pointer 00573 Buffer Buffer to retrieve count bytes from file 00574 Count Number of bytes to read into Buffer 00575 00576 NOTE 00577 This function is only called from the my_b_read() macro when there 00578 isn't enough characters in the buffer to satisfy the request. 00579 00580 IMPLEMENTATION 00581 00582 It works as follows: when a thread tries to read from a file (that 00583 is, after using all the data from the (shared) buffer), it just 00584 hangs on lock_io_cache(), wating for other threads. When the very 00585 last thread attempts a read, lock_io_cache() returns 1, the thread 00586 does actual IO and unlock_io_cache(), which signals all the waiting 00587 threads that data is in the buffer. 00588 00589 WARNING 00590 00591 When changing this function, be careful with handling file offsets 00592 (end-of_file, pos_in_file). Do not cast them to possibly smaller 00593 types than my_off_t unless you can be sure that their value fits. 00594 Same applies to differences of file offsets. (Bug #11527) 00595 00596 When changing this function, check _my_b_read(). It might need the 00597 same change. 00598 00599 RETURN 00600 0 we succeeded in reading all data 00601 1 Error: can't read requested characters 00602 */ 00603 00604 int _my_b_read_r(register IO_CACHE *info, byte *Buffer, uint Count) 00605 { 00606 my_off_t pos_in_file; 00607 uint length, diff_length, left_length; 00608 DBUG_ENTER("_my_b_read_r"); 00609 00610 if ((left_length= (uint) (info->read_end - info->read_pos))) 00611 { 00612 DBUG_ASSERT(Count >= left_length); /* User is not using my_b_read() */ 00613 memcpy(Buffer, info->read_pos, (size_t) (left_length)); 00614 Buffer+= left_length; 00615 Count-= left_length; 00616 } 00617 while (Count) 00618 { 00619 int cnt, len; 00620 00621 pos_in_file= info->pos_in_file + (info->read_end - info->buffer); 00622 diff_length= (uint) (pos_in_file & (IO_SIZE-1)); 00623 length=IO_ROUND_UP(Count+diff_length)-diff_length; 00624 length=(length <= info->read_length) ? 00625 length + IO_ROUND_DN(info->read_length - length) : 00626 length - IO_ROUND_UP(length - info->read_length) ; 00627 if (info->type != READ_FIFO && 00628 (length > (info->end_of_file - pos_in_file))) 00629 length= (uint) (info->end_of_file - pos_in_file); 00630 if (length == 0) 00631 { 00632 info->error= (int) left_length; 00633 DBUG_RETURN(1); 00634 } 00635 if (lock_io_cache(info, pos_in_file)) 00636 { 00637 info->share->active=info; 00638 if (info->seek_not_done) /* File touched, do seek */ 00639 VOID(my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0))); 00640 len=(int)my_read(info->file,info->buffer, length, info->myflags); 00641 info->read_end=info->buffer + (len == -1 ? 0 : len); 00642 info->error=(len == (int)length ? 0 : len); 00643 info->pos_in_file=pos_in_file; 00644 unlock_io_cache(info); 00645 } 00646 else 00647 { 00648 info->error= info->share->active->error; 00649 info->read_end= info->share->active->read_end; 00650 info->pos_in_file= info->share->active->pos_in_file; 00651 len= (int) (info->error == -1 ? -1 : info->read_end-info->buffer); 00652 } 00653 info->read_pos=info->buffer; 00654 info->seek_not_done=0; 00655 if (len <= 0) 00656 { 00657 info->error= (int) left_length; 00658 DBUG_RETURN(1); 00659 } 00660 cnt= ((uint) len > Count) ? (int) Count : len; 00661 memcpy(Buffer, info->read_pos, (size_t) cnt); 00662 Count -= cnt; 00663 Buffer+= cnt; 00664 left_length+= cnt; 00665 info->read_pos+= cnt; 00666 } 00667 DBUG_RETURN(0); 00668 } 00669 #endif 00670 00671 00672 /* 00673 Do sequential read from the SEQ_READ_APPEND cache 00674 we do this in three stages: 00675 - first read from info->buffer 00676 - then if there are still data to read, try the file descriptor 00677 - afterwards, if there are still data to read, try append buffer 00678 */ 00679 00680 int _my_b_seq_read(register IO_CACHE *info, byte *Buffer, uint Count) 00681 { 00682 uint length,diff_length,left_length,save_count; 00683 my_off_t max_length, pos_in_file; 00684 save_count=Count; 00685 00686 /* first, read the regular buffer */ 00687 if ((left_length=(uint) (info->read_end-info->read_pos))) 00688 { 00689 DBUG_ASSERT(Count > left_length); /* User is not using my_b_read() */ 00690 memcpy(Buffer,info->read_pos, (size_t) (left_length)); 00691 Buffer+=left_length; 00692 Count-=left_length; 00693 } 00694 lock_append_buffer(info); 00695 00696 /* pos_in_file always point on where info->buffer was read */ 00697 if ((pos_in_file=info->pos_in_file+(uint) (info->read_end - info->buffer)) >= 00698 info->end_of_file) 00699 goto read_append_buffer; 00700 00701 /* 00702 With read-append cache we must always do a seek before we read, 00703 because the write could have moved the file pointer astray 00704 */ 00705 VOID(my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0))); 00706 info->seek_not_done=0; 00707 00708 diff_length=(uint) (pos_in_file & (IO_SIZE-1)); 00709 00710 /* now the second stage begins - read from file descriptor */ 00711 if (Count >= (uint) (IO_SIZE+(IO_SIZE-diff_length))) 00712 { /* Fill first intern buffer */ 00713 uint read_length; 00714 00715 length=(Count & (uint) ~(IO_SIZE-1))-diff_length; 00716 if ((read_length=my_read(info->file,Buffer,(uint) length,info->myflags)) == 00717 (uint)-1) 00718 { 00719 info->error= -1; 00720 unlock_append_buffer(info); 00721 return 1; 00722 } 00723 Count-=read_length; 00724 Buffer+=read_length; 00725 pos_in_file+=read_length; 00726 00727 if (read_length != (uint) length) 00728 { 00729 /* 00730 We only got part of data; Read the rest of the data from the 00731 write buffer 00732 */ 00733 goto read_append_buffer; 00734 } 00735 left_length+=length; 00736 diff_length=0; 00737 } 00738 00739 max_length=info->read_length-diff_length; 00740 if (max_length > (info->end_of_file - pos_in_file)) 00741 max_length = info->end_of_file - pos_in_file; 00742 if (!max_length) 00743 { 00744 if (Count) 00745 goto read_append_buffer; 00746 length=0; /* Didn't read any more chars */ 00747 } 00748 else 00749 { 00750 length=my_read(info->file,info->buffer,(uint) max_length, 00751 info->myflags); 00752 if (length == (uint) -1) 00753 { 00754 info->error= -1; 00755 unlock_append_buffer(info); 00756 return 1; 00757 } 00758 if (length < Count) 00759 { 00760 memcpy(Buffer,info->buffer,(size_t) length); 00761 Count -= length; 00762 Buffer += length; 00763 00764 /* 00765 added the line below to make 00766 DBUG_ASSERT(pos_in_file==info->end_of_file) pass. 00767 otherwise this does not appear to be needed 00768 */ 00769 pos_in_file += length; 00770 goto read_append_buffer; 00771 } 00772 } 00773 unlock_append_buffer(info); 00774 info->read_pos=info->buffer+Count; 00775 info->read_end=info->buffer+length; 00776 info->pos_in_file=pos_in_file; 00777 memcpy(Buffer,info->buffer,(size_t) Count); 00778 return 0; 00779 00780 read_append_buffer: 00781 00782 /* 00783 Read data from the current write buffer. 00784 Count should never be == 0 here (The code will work even if count is 0) 00785 */ 00786 00787 { 00788 /* First copy the data to Count */ 00789 uint len_in_buff = (uint) (info->write_pos - info->append_read_pos); 00790 uint copy_len; 00791 uint transfer_len; 00792 00793 DBUG_ASSERT(info->append_read_pos <= info->write_pos); 00794 /* 00795 TODO: figure out if the assert below is needed or correct. 00796 */ 00797 DBUG_ASSERT(pos_in_file == info->end_of_file); 00798 copy_len=min(Count, len_in_buff); 00799 memcpy(Buffer, info->append_read_pos, copy_len); 00800 info->append_read_pos += copy_len; 00801 Count -= copy_len; 00802 if (Count) 00803 info->error = save_count - Count; 00804 00805 /* Fill read buffer with data from write buffer */ 00806 memcpy(info->buffer, info->append_read_pos, 00807 (size_t) (transfer_len=len_in_buff - copy_len)); 00808 info->read_pos= info->buffer; 00809 info->read_end= info->buffer+transfer_len; 00810 info->append_read_pos=info->write_pos; 00811 info->pos_in_file=pos_in_file+copy_len; 00812 info->end_of_file+=len_in_buff; 00813 } 00814 unlock_append_buffer(info); 00815 return Count ? 1 : 0; 00816 } 00817 00818 00819 #ifdef HAVE_AIOWAIT 00820 00821 int _my_b_async_read(register IO_CACHE *info, byte *Buffer, uint Count) 00822 { 00823 uint length,read_length,diff_length,left_length,use_length,org_Count; 00824 my_off_t max_length; 00825 my_off_t next_pos_in_file; 00826 byte *read_buffer; 00827 00828 memcpy(Buffer,info->read_pos, 00829 (size_t) (left_length=(uint) (info->read_end-info->read_pos))); 00830 Buffer+=left_length; 00831 org_Count=Count; 00832 Count-=left_length; 00833 00834 if (info->inited) 00835 { /* wait for read block */ 00836 info->inited=0; /* No more block to read */ 00837 my_aiowait(&info->aio_result); /* Wait for outstanding req */ 00838 if (info->aio_result.result.aio_errno) 00839 { 00840 if (info->myflags & MY_WME) 00841 my_error(EE_READ, MYF(ME_BELL+ME_WAITTANG), 00842 my_filename(info->file), 00843 info->aio_result.result.aio_errno); 00844 my_errno=info->aio_result.result.aio_errno; 00845 info->error= -1; 00846 return(1); 00847 } 00848 if (! (read_length = (uint) info->aio_result.result.aio_return) || 00849 read_length == (uint) -1) 00850 { 00851 my_errno=0; /* For testing */ 00852 info->error= (read_length == (uint) -1 ? -1 : 00853 (int) (read_length+left_length)); 00854 return(1); 00855 } 00856 info->pos_in_file+=(uint) (info->read_end - info->request_pos); 00857 00858 if (info->request_pos != info->buffer) 00859 info->request_pos=info->buffer; 00860 else 00861 info->request_pos=info->buffer+info->read_length; 00862 info->read_pos=info->request_pos; 00863 next_pos_in_file=info->aio_read_pos+read_length; 00864 00865 /* Check if pos_in_file is changed 00866 (_ni_read_cache may have skipped some bytes) */ 00867 00868 if (info->aio_read_pos < info->pos_in_file) 00869 { /* Fix if skipped bytes */ 00870 if (info->aio_read_pos + read_length < info->pos_in_file) 00871 { 00872 read_length=0; /* Skip block */ 00873 next_pos_in_file=info->pos_in_file; 00874 } 00875 else 00876 { 00877 my_off_t offset= (info->pos_in_file - info->aio_read_pos); 00878 info->pos_in_file=info->aio_read_pos; /* Whe are here */ 00879 info->read_pos=info->request_pos+offset; 00880 read_length-=offset; /* Bytes left from read_pos */ 00881 } 00882 } 00883 #ifndef DBUG_OFF 00884 if (info->aio_read_pos > info->pos_in_file) 00885 { 00886 my_errno=EINVAL; 00887 return(info->read_length= -1); 00888 } 00889 #endif 00890 /* Copy found bytes to buffer */ 00891 length=min(Count,read_length); 00892 memcpy(Buffer,info->read_pos,(size_t) length); 00893 Buffer+=length; 00894 Count-=length; 00895 left_length+=length; 00896 info->read_end=info->rc_pos+read_length; 00897 info->read_pos+=length; 00898 } 00899 else 00900 next_pos_in_file=(info->pos_in_file+ (uint) 00901 (info->read_end - info->request_pos)); 00902 00903 /* If reading large blocks, or first read or read with skip */ 00904 if (Count) 00905 { 00906 if (next_pos_in_file == info->end_of_file) 00907 { 00908 info->error=(int) (read_length+left_length); 00909 return 1; 00910 } 00911 VOID(my_seek(info->file,next_pos_in_file,MY_SEEK_SET,MYF(0))); 00912 read_length=IO_SIZE*2- (uint) (next_pos_in_file & (IO_SIZE-1)); 00913 if (Count < read_length) 00914 { /* Small block, read to cache */ 00915 if ((read_length=my_read(info->file,info->request_pos, 00916 read_length, info->myflags)) == (uint) -1) 00917 return info->error= -1; 00918 use_length=min(Count,read_length); 00919 memcpy(Buffer,info->request_pos,(size_t) use_length); 00920 info->read_pos=info->request_pos+Count; 00921 info->read_end=info->request_pos+read_length; 00922 info->pos_in_file=next_pos_in_file; /* Start of block in cache */ 00923 next_pos_in_file+=read_length; 00924 00925 if (Count != use_length) 00926 { /* Didn't find hole block */ 00927 if (info->myflags & (MY_WME | MY_FAE | MY_FNABP) && Count != org_Count) 00928 my_error(EE_EOFERR, MYF(ME_BELL+ME_WAITTANG), 00929 my_filename(info->file),my_errno); 00930 info->error=(int) (read_length+left_length); 00931 return 1; 00932 } 00933 } 00934 else 00935 { /* Big block, don't cache it */ 00936 if ((read_length=my_read(info->file,Buffer,(uint) Count,info->myflags)) 00937 != Count) 00938 { 00939 info->error= read_length == (uint) -1 ? -1 : read_length+left_length; 00940 return 1; 00941 } 00942 info->read_pos=info->read_end=info->request_pos; 00943 info->pos_in_file=(next_pos_in_file+=Count); 00944 } 00945 } 00946 00947 /* Read next block with asyncronic io */ 00948 max_length=info->end_of_file - next_pos_in_file; 00949 diff_length=(next_pos_in_file & (IO_SIZE-1)); 00950 00951 if (max_length > (my_off_t) info->read_length - diff_length) 00952 max_length= (my_off_t) info->read_length - diff_length; 00953 if (info->request_pos != info->buffer) 00954 read_buffer=info->buffer; 00955 else 00956 read_buffer=info->buffer+info->read_length; 00957 info->aio_read_pos=next_pos_in_file; 00958 if (max_length) 00959 { 00960 info->aio_result.result.aio_errno=AIO_INPROGRESS; /* Marker for test */ 00961 DBUG_PRINT("aioread",("filepos: %ld length: %ld", 00962 (ulong) next_pos_in_file,(ulong) max_length)); 00963 if (aioread(info->file,read_buffer,(int) max_length, 00964 (my_off_t) next_pos_in_file,MY_SEEK_SET, 00965 &info->aio_result.result)) 00966 { /* Skip async io */ 00967 my_errno=errno; 00968 DBUG_PRINT("error",("got error: %d, aio_result: %d from aioread, async skipped", 00969 errno, info->aio_result.result.aio_errno)); 00970 if (info->request_pos != info->buffer) 00971 { 00972 bmove(info->buffer,info->request_pos, 00973 (uint) (info->read_end - info->read_pos)); 00974 info->request_pos=info->buffer; 00975 info->read_pos-=info->read_length; 00976 info->read_end-=info->read_length; 00977 } 00978 info->read_length=info->buffer_length; /* Use hole buffer */ 00979 info->read_function=_my_b_read; /* Use normal IO_READ next */ 00980 } 00981 else 00982 info->inited=info->aio_result.pending=1; 00983 } 00984 return 0; /* Block read, async in use */ 00985 } /* _my_b_async_read */ 00986 #endif 00987 00988 00989 /* Read one byte when buffer is empty */ 00990 00991 int _my_b_get(IO_CACHE *info) 00992 { 00993 byte buff; 00994 IO_CACHE_CALLBACK pre_read,post_read; 00995 if ((pre_read = info->pre_read)) 00996 (*pre_read)(info); 00997 if ((*(info)->read_function)(info,&buff,1)) 00998 return my_b_EOF; 00999 if ((post_read = info->post_read)) 01000 (*post_read)(info); 01001 return (int) (uchar) buff; 01002 } 01003 01004 /* Returns != 0 if error on write */ 01005 01006 int _my_b_write(register IO_CACHE *info, const byte *Buffer, uint Count) 01007 { 01008 uint rest_length,length; 01009 01010 if (info->pos_in_file+info->buffer_length > info->end_of_file) 01011 { 01012 my_errno=errno=EFBIG; 01013 return info->error = -1; 01014 } 01015 01016 rest_length=(uint) (info->write_end - info->write_pos); 01017 memcpy(info->write_pos,Buffer,(size_t) rest_length); 01018 Buffer+=rest_length; 01019 Count-=rest_length; 01020 info->write_pos+=rest_length; 01021 if (my_b_flush_io_cache(info,1)) 01022 return 1; 01023 if (Count >= IO_SIZE) 01024 { /* Fill first intern buffer */ 01025 length=Count & (uint) ~(IO_SIZE-1); 01026 if (info->seek_not_done) 01027 { /* File touched, do seek */ 01028 VOID(my_seek(info->file,info->pos_in_file,MY_SEEK_SET,MYF(0))); 01029 info->seek_not_done=0; 01030 } 01031 if (my_write(info->file,Buffer,(uint) length,info->myflags | MY_NABP)) 01032 return info->error= -1; 01033 Count-=length; 01034 Buffer+=length; 01035 info->pos_in_file+=length; 01036 } 01037 memcpy(info->write_pos,Buffer,(size_t) Count); 01038 info->write_pos+=Count; 01039 return 0; 01040 } 01041 01042 01043 /* 01044 Append a block to the write buffer. 01045 This is done with the buffer locked to ensure that we don't read from 01046 the write buffer before we are ready with it. 01047 */ 01048 01049 int my_b_append(register IO_CACHE *info, const byte *Buffer, uint Count) 01050 { 01051 uint rest_length,length; 01052 01053 lock_append_buffer(info); 01054 rest_length=(uint) (info->write_end - info->write_pos); 01055 if (Count <= rest_length) 01056 goto end; 01057 memcpy(info->write_pos,Buffer,(size_t) rest_length); 01058 Buffer+=rest_length; 01059 Count-=rest_length; 01060 info->write_pos+=rest_length; 01061 if (my_b_flush_io_cache(info,0)) 01062 { 01063 unlock_append_buffer(info); 01064 return 1; 01065 } 01066 if (Count >= IO_SIZE) 01067 { /* Fill first intern buffer */ 01068 length=Count & (uint) ~(IO_SIZE-1); 01069 if (my_write(info->file,Buffer,(uint) length,info->myflags | MY_NABP)) 01070 { 01071 unlock_append_buffer(info); 01072 return info->error= -1; 01073 } 01074 Count-=length; 01075 Buffer+=length; 01076 info->end_of_file+=length; 01077 } 01078 01079 end: 01080 memcpy(info->write_pos,Buffer,(size_t) Count); 01081 info->write_pos+=Count; 01082 unlock_append_buffer(info); 01083 return 0; 01084 } 01085 01086 01087 int my_b_safe_write(IO_CACHE *info, const byte *Buffer, uint Count) 01088 { 01089 /* 01090 Sasha: We are not writing this with the ? operator to avoid hitting 01091 a possible compiler bug. At least gcc 2.95 cannot deal with 01092 several layers of ternary operators that evaluated comma(,) operator 01093 expressions inside - I do have a test case if somebody wants it 01094 */ 01095 if (info->type == SEQ_READ_APPEND) 01096 return my_b_append(info, Buffer, Count); 01097 return my_b_write(info, Buffer, Count); 01098 } 01099 01100 01101 /* 01102 Write a block to disk where part of the data may be inside the record 01103 buffer. As all write calls to the data goes through the cache, 01104 we will never get a seek over the end of the buffer 01105 */ 01106 01107 int my_block_write(register IO_CACHE *info, const byte *Buffer, uint Count, 01108 my_off_t pos) 01109 { 01110 uint length; 01111 int error=0; 01112 01113 if (pos < info->pos_in_file) 01114 { 01115 /* Of no overlap, write everything without buffering */ 01116 if (pos + Count <= info->pos_in_file) 01117 return my_pwrite(info->file, Buffer, Count, pos, 01118 info->myflags | MY_NABP); 01119 /* Write the part of the block that is before buffer */ 01120 length= (uint) (info->pos_in_file - pos); 01121 if (my_pwrite(info->file, Buffer, length, pos, info->myflags | MY_NABP)) 01122 info->error=error=-1; 01123 Buffer+=length; 01124 pos+= length; 01125 Count-= length; 01126 #ifndef HAVE_PREAD 01127 info->seek_not_done=1; 01128 #endif 01129 } 01130 01131 /* Check if we want to write inside the used part of the buffer.*/ 01132 length= (uint) (info->write_end - info->buffer); 01133 if (pos < info->pos_in_file + length) 01134 { 01135 uint offset= (uint) (pos - info->pos_in_file); 01136 length-=offset; 01137 if (length > Count) 01138 length=Count; 01139 memcpy(info->buffer+offset, Buffer, length); 01140 Buffer+=length; 01141 Count-= length; 01142 /* Fix length of buffer if the new data was larger */ 01143 if (info->buffer+length > info->write_pos) 01144 info->write_pos=info->buffer+length; 01145 if (!Count) 01146 return (error); 01147 } 01148 /* Write at the end of the current buffer; This is the normal case */ 01149 if (_my_b_write(info, Buffer, Count)) 01150 error= -1; 01151 return error; 01152 } 01153 01154 01155 /* Flush write cache */ 01156 01157 #ifdef THREAD 01158 #define LOCK_APPEND_BUFFER if (need_append_buffer_lock) \ 01159 lock_append_buffer(info); 01160 #define UNLOCK_APPEND_BUFFER if (need_append_buffer_lock) \ 01161 unlock_append_buffer(info); 01162 #else 01163 #define LOCK_APPEND_BUFFER 01164 #define UNLOCK_APPEND_BUFFER 01165 #endif 01166 01167 01168 int my_b_flush_io_cache(IO_CACHE *info, int need_append_buffer_lock) 01169 { 01170 uint length; 01171 my_bool append_cache; 01172 my_off_t pos_in_file; 01173 DBUG_ENTER("my_b_flush_io_cache"); 01174 01175 if (!(append_cache = (info->type == SEQ_READ_APPEND))) 01176 need_append_buffer_lock=0; 01177 01178 if (info->type == WRITE_CACHE || append_cache) 01179 { 01180 if (info->file == -1) 01181 { 01182 if (real_open_cached_file(info)) 01183 DBUG_RETURN((info->error= -1)); 01184 } 01185 LOCK_APPEND_BUFFER; 01186 01187 if ((length=(uint) (info->write_pos - info->write_buffer))) 01188 { 01189 pos_in_file=info->pos_in_file; 01190 /* 01191 If we have append cache, we always open the file with 01192 O_APPEND which moves the pos to EOF automatically on every write 01193 */ 01194 if (!append_cache && info->seek_not_done) 01195 { /* File touched, do seek */ 01196 if (my_seek(info->file,pos_in_file,MY_SEEK_SET,MYF(0)) == 01197 MY_FILEPOS_ERROR) 01198 { 01199 UNLOCK_APPEND_BUFFER; 01200 DBUG_RETURN((info->error= -1)); 01201 } 01202 if (!append_cache) 01203 info->seek_not_done=0; 01204 } 01205 if (!append_cache) 01206 info->pos_in_file+=length; 01207 info->write_end= (info->write_buffer+info->buffer_length- 01208 ((pos_in_file+length) & (IO_SIZE-1))); 01209 01210 if (my_write(info->file,info->write_buffer,length, 01211 info->myflags | MY_NABP)) 01212 info->error= -1; 01213 else 01214 info->error= 0; 01215 if (!append_cache) 01216 { 01217 set_if_bigger(info->end_of_file,(pos_in_file+length)); 01218 } 01219 else 01220 { 01221 info->end_of_file+=(info->write_pos-info->append_read_pos); 01222 DBUG_ASSERT(info->end_of_file == my_tell(info->file,MYF(0))); 01223 } 01224 01225 info->append_read_pos=info->write_pos=info->write_buffer; 01226 ++info->disk_writes; 01227 UNLOCK_APPEND_BUFFER; 01228 DBUG_RETURN(info->error); 01229 } 01230 } 01231 #ifdef HAVE_AIOWAIT 01232 else if (info->type != READ_NET) 01233 { 01234 my_aiowait(&info->aio_result); /* Wait for outstanding req */ 01235 info->inited=0; 01236 } 01237 #endif 01238 UNLOCK_APPEND_BUFFER; 01239 DBUG_RETURN(0); 01240 } 01241 01242 /* 01243 Free an IO_CACHE object 01244 01245 SYNOPSOS 01246 end_io_cache() 01247 info IO_CACHE Handle to free 01248 01249 NOTES 01250 It's currently safe to call this if one has called init_io_cache() 01251 on the 'info' object, even if init_io_cache() failed. 01252 This function is also safe to call twice with the same handle. 01253 01254 RETURN 01255 0 ok 01256 # Error 01257 */ 01258 01259 int end_io_cache(IO_CACHE *info) 01260 { 01261 int error=0; 01262 IO_CACHE_CALLBACK pre_close; 01263 DBUG_ENTER("end_io_cache"); 01264 DBUG_PRINT("enter",("cache: 0x%lx", (ulong) info)); 01265 01266 #ifdef THREAD 01267 /* 01268 if IO_CACHE is shared between several threads, only one 01269 thread needs to call end_io_cache() - just as init_io_cache() 01270 should be called only once and then memcopy'ed 01271 */ 01272 if (info->share) 01273 { 01274 pthread_cond_destroy(&info->share->cond); 01275 pthread_mutex_destroy(&info->share->mutex); 01276 info->share=0; 01277 } 01278 #endif 01279 01280 if ((pre_close=info->pre_close)) 01281 { 01282 (*pre_close)(info); 01283 info->pre_close= 0; 01284 } 01285 if (info->alloced_buffer) 01286 { 01287 info->alloced_buffer=0; 01288 if (info->file != -1) /* File doesn't exist */ 01289 error= my_b_flush_io_cache(info,1); 01290 my_free((gptr) info->buffer,MYF(MY_WME)); 01291 info->buffer=info->read_pos=(byte*) 0; 01292 } 01293 if (info->type == SEQ_READ_APPEND) 01294 { 01295 /* Destroy allocated mutex */ 01296 info->type= TYPE_NOT_SET; 01297 #ifdef THREAD 01298 pthread_mutex_destroy(&info->append_buffer_lock); 01299 #endif 01300 } 01301 DBUG_RETURN(error); 01302 } /* end_io_cache */ 01303 01304 01305 /********************************************************************** 01306 Testing of MF_IOCACHE 01307 **********************************************************************/ 01308 01309 #ifdef MAIN 01310 01311 #include <my_dir.h> 01312 01313 void die(const char* fmt, ...) 01314 { 01315 va_list va_args; 01316 va_start(va_args,fmt); 01317 fprintf(stderr,"Error:"); 01318 vfprintf(stderr, fmt,va_args); 01319 fprintf(stderr,", errno=%d\n", errno); 01320 exit(1); 01321 } 01322 01323 int open_file(const char* fname, IO_CACHE* info, int cache_size) 01324 { 01325 int fd; 01326 if ((fd=my_open(fname,O_CREAT | O_RDWR,MYF(MY_WME))) < 0) 01327 die("Could not open %s", fname); 01328 if (init_io_cache(info, fd, cache_size, SEQ_READ_APPEND, 0,0,MYF(MY_WME))) 01329 die("failed in init_io_cache()"); 01330 return fd; 01331 } 01332 01333 void close_file(IO_CACHE* info) 01334 { 01335 end_io_cache(info); 01336 my_close(info->file, MYF(MY_WME)); 01337 } 01338 01339 int main(int argc, char** argv) 01340 { 01341 IO_CACHE sra_cache; /* SEQ_READ_APPEND */ 01342 MY_STAT status; 01343 const char* fname="/tmp/iocache.test"; 01344 int cache_size=16384; 01345 char llstr_buf[22]; 01346 int max_block,total_bytes=0; 01347 int i,num_loops=100,error=0; 01348 char *p; 01349 char* block, *block_end; 01350 MY_INIT(argv[0]); 01351 max_block = cache_size*3; 01352 if (!(block=(char*)my_malloc(max_block,MYF(MY_WME)))) 01353 die("Not enough memory to allocate test block"); 01354 block_end = block + max_block; 01355 for (p = block,i=0; p < block_end;i++) 01356 { 01357 *p++ = (char)i; 01358 } 01359 if (my_stat(fname,&status, MYF(0)) && 01360 my_delete(fname,MYF(MY_WME))) 01361 { 01362 die("Delete of %s failed, aborting", fname); 01363 } 01364 open_file(fname,&sra_cache, cache_size); 01365 for (i = 0; i < num_loops; i++) 01366 { 01367 char buf[4]; 01368 int block_size = abs(rand() % max_block); 01369 int4store(buf, block_size); 01370 if (my_b_append(&sra_cache,buf,4) || 01371 my_b_append(&sra_cache, block, block_size)) 01372 die("write failed"); 01373 total_bytes += 4+block_size; 01374 } 01375 close_file(&sra_cache); 01376 my_free(block,MYF(MY_WME)); 01377 if (!my_stat(fname,&status,MYF(MY_WME))) 01378 die("%s failed to stat, but I had just closed it,\ 01379 wonder how that happened"); 01380 printf("Final size of %s is %s, wrote %d bytes\n",fname, 01381 llstr(status.st_size,llstr_buf), 01382 total_bytes); 01383 my_delete(fname, MYF(MY_WME)); 01384 /* check correctness of tests */ 01385 if (total_bytes != status.st_size) 01386 { 01387 fprintf(stderr,"Not the same number of bytes acutally in file as bytes \ 01388 supposedly written\n"); 01389 error=1; 01390 } 01391 exit(error); 01392 return 0; 01393 } 01394 #endif
1.4.7

