forked from enlightenment/efl
* 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:
parent
ea831b3738
commit
135a0f570a
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(©->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(©->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(©->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(©->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 ©->common;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue