~drizzle-trunk/drizzle/development

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