* eio: add eio_file_copy (using splice when available and

fallback to mmap/write with huge tlb when not).

That what an eio_cp would look like :

#include <Ecore.h>
#include <Eio.h>

static void
_test_progress_cb(void *data, const Eio_Progress *info)
{
   printf("%f (%zi / %zi octets)\n", info->percent, info->current, info->max);
}

static void
_test_done_cb(void *data)
{
   printf("copy done\n");
   ecore_main_loop_quit();
}

static void
_test_error_cb(int error, void *data)
{
   fprintf(stderr, "error: [%s]\n", strerror(error));
   ecore_main_loop_quit();
}

int
main(int argc, char **argv)
{
   Eio_File *cp;

   if (argc != 3)
     {
        fprintf(stderr, "eio_cp source_file destination_file\n");
        return -1;
     }

   ecore_init();
   eio_init();

   cp = eio_file_copy(argv[1], argv[2],
                      _test_progress_cb,
                      _test_done_cb,
                      _test_error_cb,
                      NULL);

   ecore_main_loop_begin();

   eio_shutdown();
   ecore_shutdown();

   return 0;
}




SVN revision: 52787
This commit is contained in:
Cedric BAIL 2010-09-26 21:47:48 +00:00
parent ea831b3738
commit 135a0f570a
4 changed files with 318 additions and 16 deletions

View File

@ -74,7 +74,6 @@ AC_HEADER_TIME
### Checks for types
### Checks for structures
### Checks for linker characteristics
@ -109,6 +108,22 @@ AC_SUBST(EFL_EIO_BUILD)
AC_FUNC_ALLOCA
AC_CHECK_FUNCS(strlcpy)
have_splice="no"
AC_TRY_LINK([
#if defined(HAVE_UNISTD_H)
#include <unistd.h>
#endif
#include <fcntl.h>],
[long ret = splice(0,0,1,0,400,0);],
have_splice=yes,
have_splice=no)
AC_MSG_CHECKING([whether to use splice for file copy])
AC_MSG_RESULT(${have_splice})
if test "x${have_splice}" = "xyes"; then
AC_DEFINE([EFL_HAVE_SPLICE], [1], [Define to mention that splice syscall is supported])
fi
AC_OUTPUT([
eio.pc
Makefile

View File

@ -87,9 +87,12 @@ typedef void (*Eio_Error_Cb)(int error, void *data);
struct _Eio_Progress
{
size_t current;
size_t max;
off_t current;
off_t max;
float percent;
const char *source;
const char *dest;
};
EAPI int eio_init(void);

View File

@ -19,6 +19,9 @@
* if not, see <http://www.gnu.org/licenses/>.
*/
#define _FILE_OFFSET_BITS 64
#define _GNU_SOURCE
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
@ -40,11 +43,19 @@
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <pthread.h>
#include "eio_private.h"
#include "Eio.h"
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static Eina_Trash *trash = NULL;
static int trash_count = 0;
static void
_eio_file_heavy(Ecore_Thread *thread, void *data)
{
@ -55,7 +66,7 @@ _eio_file_heavy(Ecore_Thread *thread, void *data)
ls = eina_file_ls(async->ls.directory);
if (!ls)
{
ecore_thread_cancel(thread);
eio_file_thread_error(&async->ls.common);
return ;
}
@ -84,8 +95,7 @@ _eio_file_notify(Ecore_Thread *thread __UNUSED__, void *msg_data, void *data)
Eio_File_Char_Ls *async = data;
const char *file = msg_data;
if (async->main_cb)
async->main_cb((void*) async->ls.common.data, file);
async->main_cb((void*) async->ls.common.data, file);
eina_stringshare_del(file);
}
@ -100,7 +110,7 @@ _eio_file_direct_heavy(Ecore_Thread *thread, void *data)
ls = eina_file_direct_ls(async->ls.directory);
if (!ls)
{
ecore_thread_cancel(thread);
eio_file_thread_error(&async->ls.common);
return ;
}
@ -137,8 +147,7 @@ _eio_file_direct_notify(Ecore_Thread *thread __UNUSED__, void *msg_data, void *d
Eio_File_Direct_Ls *async = data;
Eina_File_Direct_Info *info = msg_data;
if (async->main_cb)
async->main_cb((void*) async->ls.common.data, info);
async->main_cb((void*) async->ls.common.data, info);
free(info);
}
@ -148,8 +157,7 @@ _eio_file_end(void *data)
{
Eio_File_Ls *async = data;
if (async->common.done_cb)
async->common.done_cb((void*) async->common.data);
async->common.done_cb((void*) async->common.data);
eina_stringshare_del(async->directory);
free(async);
@ -158,9 +166,7 @@ _eio_file_end(void *data)
static void
_eio_file_error(void *data)
{
Eio_File_Ls *async;
async = data;
Eio_File_Ls *async = data;
eio_file_error(&async->common);
@ -168,6 +174,215 @@ _eio_file_error(void *data)
free(async);
}
static Eina_Bool
_eio_file_write(int fd, void *mem, ssize_t length)
{
ssize_t count;
if (length == 0) return EINA_TRUE;
count = write(fd, mem, length);
if (count != length) return EINA_FALSE;
return EINA_TRUE;
}
static void
_eio_file_copy_send(Ecore_Thread *thread, Eio_File_Copy *copy, off_t current, off_t max)
{
Eio_Progress *progress;
if (copy->progress_cb == NULL)
return ;
pthread_mutex_lock(&lock);
progress = eina_trash_pop(&trash);
if (progress) trash_count--;
pthread_mutex_unlock(&lock);
if (!progress) progress = malloc(sizeof (Eio_Progress));
if (!progress) return ;
progress->current = current;
progress->max = max;
progress->percent = (float) current * 100.0 / (float) max;
ecore_thread_feedback(thread, progress);
}
#ifndef MAP_HUGETLB
# define MAP_HUGETLB 0
#endif
static Eina_Bool
_eio_file_copy_mmap(Ecore_Thread *thread, Eio_File_Copy *copy, int in, int out, off_t size)
{
char *m = MAP_FAILED;
off_t i;
for (i = 0; i < size; i += EIO_PACKET_SIZE * EIO_PACKET_COUNT)
{
int j;
int k;
int c;
m = mmap(NULL, EIO_PACKET_SIZE * EIO_PACKET_COUNT, PROT_READ, MAP_FILE | MAP_HUGETLB | MAP_SHARED, in, i);
if (m == MAP_FAILED)
goto on_error;
madvise(m, EIO_PACKET_SIZE * EIO_PACKET_COUNT, MADV_SEQUENTIAL);
c = size - i;
if (c - EIO_PACKET_SIZE * EIO_PACKET_COUNT > 0)
c = EIO_PACKET_SIZE * EIO_PACKET_COUNT;
else
c = size - i;
for (j = EIO_PACKET_SIZE, k = 0; j < c; k = j, j += EIO_PACKET_SIZE)
{
if (!_eio_file_write(out, m + k, EIO_PACKET_SIZE))
goto on_error;
_eio_file_copy_send(thread, copy, i + j, size);
}
if (!_eio_file_write(out, m + k, c - k))
goto on_error;
_eio_file_copy_send(thread, copy, i + c, size);
munmap(m, EIO_PACKET_SIZE * EIO_PACKET_COUNT);
m = MAP_FAILED;
}
return EINA_TRUE;
on_error:
if (m != MAP_FAILED) munmap(m, EIO_PACKET_SIZE * EIO_PACKET_COUNT);
return EINA_FALSE;
}
#ifdef EFL_HAVE_SPLICE
static int
_eio_file_copy_splice(Ecore_Thread *thread, Eio_File_Copy *copy, int in, int out, off_t size)
{
int result = 0;
off_t count;
off_t i;
int pipefd[2];
if (pipe(pipefd) < 0)
return -1;
for (i = 0; i < size; i += count)
{
count = splice(in, 0, pipefd[1], NULL, EIO_PACKET_SIZE * EIO_PACKET_COUNT, SPLICE_F_MORE | SPLICE_F_MOVE);
if (count < 0) goto on_error;
count = splice(pipefd[0], NULL, out, 0, count, SPLICE_F_MORE | SPLICE_F_MOVE);
if (count < 0) goto on_error;
_eio_file_copy_send(thread, copy, i, size);
}
result = 1;
on_error:
if (result != 1 && (errno == EBADF || errno == EINVAL)) result = -1;
close(pipefd[0]);
close(pipefd[1]);
return result;
}
#endif
static void
_eio_file_copy_heavy(Ecore_Thread *thread, void *data)
{
Eio_File_Copy *copy;
struct stat buf;
int result = -1;
int in = -1;
int out = -1;
copy = data;
in = open(copy->source, O_RDONLY);
if (in < 0)
{
eio_file_thread_error(&copy->common);
return ;
}
if (fstat(in, &buf) < 0)
goto on_error;
/* open write */
out = open(copy->dest, O_WRONLY | O_CREAT | O_TRUNC, buf.st_mode);
if (out < 0)
goto on_error;
#ifdef EFL_HAVE_SPLICE
result = _eio_file_copy_splice(thread, copy, in, out, buf.st_size);
if (result == 0)
goto on_error;
#endif
if (result == -1 && !_eio_file_copy_mmap(thread, copy, in, out, buf.st_size))
goto on_error;
close(out);
close(in);
return ;
on_error:
eio_file_thread_error(&copy->common);
if (in >= 0) close(in);
if (out >= 0) close(out);
if (out >= 0)
unlink(copy->dest);
return ;
}
static void
_eio_file_copy_notify(Ecore_Thread *thread __UNUSED__, void *msg_data, void *data)
{
Eio_File_Copy *copy = data;
Eio_Progress *progress = msg_data;
copy->progress_cb((void *) copy->common.data, progress);
pthread_mutex_lock(&lock);
eina_trash_push(&trash, progress);
trash_count++;
pthread_mutex_unlock(&lock);
}
static void
_eio_file_copy_end(void *data)
{
Eio_File_Copy *copy = data;
copy->common.done_cb((void*) copy->common.data);
eina_stringshare_del(copy->source);
eina_stringshare_del(copy->dest);
free(copy);
}
static void
_eio_file_copy_error(void *data)
{
Eio_File_Copy *copy = data;
eio_file_error(&copy->common);
eina_stringshare_del(copy->source);
eina_stringshare_del(copy->dest);
free(copy);
}
/**
* @brief List content of a directory without locking your app.
* @param dir The directory to list.
@ -190,7 +405,7 @@ eio_file_ls(const char *dir,
{
Eio_File_Char_Ls *async = NULL;
if (!dir)
if (!dir || !main_cb || !done_cb || !error_cb)
return NULL;
async = malloc(sizeof (Eio_File_Char_Ls));
@ -235,7 +450,7 @@ eio_file_direct_ls(const char *dir,
{
Eio_File_Direct_Ls *async = NULL;
if (!dir)
if (!dir || !main_cb || !done_cb || !error_cb)
return NULL;
async = malloc(sizeof (Eio_File_Direct_Ls));
@ -258,9 +473,63 @@ eio_file_direct_ls(const char *dir,
return &async->ls.common;
}
/**
* @brief Cancel any Eio_File.
* @param ls The asynchronous IO operation to cancel.
* @return EINA_FALSE if the destruction is delayed, EINA_TRUE if it's done.
*
* This will cancel any kind of IO operation and cleanup the mess. This means
* that it could take time to cancel an IO.
*/
EAPI Eina_Bool
eio_file_cancel(Eio_File *ls)
{
return ecore_thread_cancel(ls->thread);
}
/**
* @brief Copy a file asynchronously
* @param source Should be the name of the file to copy the data from.
* @param dest Should be the name of the file to copy the data to.
* @param progress_cb Callback called to know the progress of the copy.
* @param done_cb Callback called when the copy is done.
* @param error_cb Callback called when something goes wrong.
* @param data Private data given to callback.
*
* This function will copy a file from source to dest. It will try to use splice
* if possible, if not it will fallback to mmap/write.
*/
EAPI Eio_File *
eio_file_copy(const char *source,
const char *dest,
Eio_Progress_Cb progress_cb,
Eio_Done_Cb done_cb,
Eio_Error_Cb error_cb,
const void *data)
{
Eio_File_Copy *copy = NULL;
if (!source || !dest || !done_cb || !error_cb)
return NULL;
copy = malloc(sizeof (Eio_File_Copy));
if (!copy) return NULL;
copy->progress_cb = progress_cb;
copy->source = eina_stringshare_add(source);
copy->dest = eina_stringshare_add(dest);
if (!eio_long_file_set(&copy->common,
done_cb,
error_cb,
data,
_eio_file_copy_heavy,
_eio_file_copy_notify,
_eio_file_copy_end,
_eio_file_copy_error))
return NULL;
return &copy->common;
}

View File

@ -9,12 +9,17 @@
#include "Eio.h"
/* Huge TLB == 16M on most system */
#define EIO_PACKET_SIZE 65536
#define EIO_PACKET_COUNT 256
typedef struct _Eio_File_Ls Eio_File_Ls;
typedef struct _Eio_File_Direct_Ls Eio_File_Direct_Ls;
typedef struct _Eio_File_Char_Ls Eio_File_Char_Ls;
typedef struct _Eio_File_Mkdir Eio_File_Mkdir;
typedef struct _Eio_File_Unlink Eio_File_Unlink;
typedef struct _Eio_File_Stat Eio_File_Stat;
typedef struct _Eio_File_Copy Eio_File_Copy;
struct _Eio_File
{
@ -74,6 +79,16 @@ struct _Eio_File_Stat
const char *path;
};
struct _Eio_File_Copy
{
Eio_File common;
Eio_Progress_Cb progress_cb;
const char *source;
const char *dest;
};
/* Be aware that ecore_thread_run could call cancel_cb if something goes wrong. */
Eina_Bool eio_file_set(Eio_File *common,
Eio_Done_Cb done_cb,