From 8ab02314baa4bf6fd1e3769b7222943a7084db28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20H=C3=B6gberg?= Date: Mon, 9 Oct 2017 08:59:32 +0200 Subject: Add zlib:set_controlling_process/2 --- erts/doc/src/zlib.xml | 14 ++++++++ erts/emulator/nifs/common/zlib_nif.c | 64 +++++++++++++++++++++++++++++------ erts/preloaded/ebin/zlib.beam | Bin 19492 -> 19784 bytes erts/preloaded/src/zlib.erl | 11 +++++- lib/kernel/test/zlib_SUITE.erl | 26 ++++++++++---- 5 files changed, 97 insertions(+), 18 deletions(-) diff --git a/erts/doc/src/zlib.xml b/erts/doc/src/zlib.xml index f5cc1b1e64..6f4c42da27 100644 --- a/erts/doc/src/zlib.xml +++ b/erts/doc/src/zlib.xml @@ -71,6 +71,11 @@ list_to_binary([Compressed|Last]) called prior to a call to inflate/2. + not_on_controlling_process + The stream was used by a process that doesn't control it. Use + + set_controlling_process/2 if you need to transfer + a stream to a different process. data_error The data contains errors. @@ -739,6 +744,15 @@ loop(Z, Handler, {finished, Output}) -> + + + Transfers ownership of a zlib stream. + +

Changes the controlling process of Z to + Pid, which must be a local process.

+
+
+ Uncompress data with standard zlib functionality. diff --git a/erts/emulator/nifs/common/zlib_nif.c b/erts/emulator/nifs/common/zlib_nif.c index a9c5b05e47..fa29b4fb71 100644 --- a/erts/emulator/nifs/common/zlib_nif.c +++ b/erts/emulator/nifs/common/zlib_nif.c @@ -106,6 +106,7 @@ typedef struct { int inflateChunk_buffer_size; ErlNifPid controlling_process; + ErlNifMutex *controller_lock; ErlNifIOQueue *input_queue; @@ -117,6 +118,8 @@ typedef struct { static ERL_NIF_TERM zlib_open(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM zlib_close(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM zlib_set_controller(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); + static ERL_NIF_TERM zlib_deflateInit(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM zlib_deflateSetDictionary(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM zlib_deflateReset(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); @@ -143,6 +146,11 @@ static ERL_NIF_TERM zlib_setBufSize(ErlNifEnv *env, int argc, const ERL_NIF_TERM static ERL_NIF_TERM zlib_enqueue_input(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]); static ErlNifFunc nif_funcs[] = { + {"close_nif", 1, zlib_close}, + {"open_nif", 0, zlib_open}, + + {"set_controller_nif", 2, zlib_set_controller}, + /* deflate */ {"deflateInit_nif", 6, zlib_deflateInit}, {"deflateSetDictionary_nif", 2, zlib_deflateSetDictionary}, @@ -162,10 +170,6 @@ static ErlNifFunc nif_funcs[] = { /* running checksum */ {"crc32_nif", 1, zlib_crc32}, - /* open & close */ - {"close_nif", 1, zlib_close}, - {"open_nif", 0, zlib_open}, - /* The stash keeps a single term alive across calls, and is used in * exception_on_need_dict/1 to retain the old error behavior, and for * saving data flushed through deflateParams/3. */ @@ -281,9 +285,7 @@ static ERL_NIF_TERM zlib_return(ErlNifEnv *env, int code) { return reason; } -static void gc_zlib(ErlNifEnv *env, void* data) { - zlib_data_t *d = (zlib_data_t*)data; - +static void zlib_internal_close(zlib_data_t *d) { if(d->state == ST_DEFLATE) { deflateEnd(&d->s); } else if(d->state == ST_INFLATE) { @@ -291,8 +293,6 @@ static void gc_zlib(ErlNifEnv *env, void* data) { } if(d->state != ST_CLOSED) { - enif_ioq_destroy(d->input_queue); - if(d->stash_env != NULL) { enif_free_env(d->stash_env); } @@ -301,17 +301,36 @@ static void gc_zlib(ErlNifEnv *env, void* data) { } } +static void gc_zlib(ErlNifEnv *env, void* data) { + zlib_data_t *d = (zlib_data_t*)data; + + enif_mutex_destroy(d->controller_lock); + enif_ioq_destroy(d->input_queue); + + zlib_internal_close(d); + + (void)env; +} + static int get_zlib_data(ErlNifEnv *env, ERL_NIF_TERM opaque, zlib_data_t **d) { return enif_get_resource(env, opaque, rtype_zlib, (void **)d); } static int zlib_process_check(ErlNifEnv *env, zlib_data_t *d) { + int is_controlling_process; ErlNifPid current_process; enif_self(env, ¤t_process); - return enif_is_identical(enif_make_pid(env, ¤t_process), + enif_mutex_lock(d->controller_lock); + + is_controlling_process = enif_is_identical( + enif_make_pid(env, ¤t_process), enif_make_pid(env, &d->controlling_process)); + + enif_mutex_unlock(d->controller_lock); + + return is_controlling_process; } static void zlib_reset_input(zlib_data_t *d) { @@ -516,6 +535,8 @@ static ERL_NIF_TERM zlib_open(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[ d->input_queue = enif_ioq_create(ERL_NIF_IOQ_NORMAL); + d->controller_lock = enif_mutex_create("zlib_controller_lock"); + d->s.zalloc = zlib_alloc; d->s.zfree = zlib_free; d->s.opaque = d; @@ -556,7 +577,28 @@ static ERL_NIF_TERM zlib_close(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv return enif_raise_exception(env, am_not_initialized); } - gc_zlib(env, d); + zlib_internal_close(d); + + return am_ok; +} + +static ERL_NIF_TERM zlib_set_controller(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) { + zlib_data_t *d; + + ErlNifPid new_owner; + + if(argc != 2 || !get_zlib_data(env, argv[0], &d) + || !enif_get_local_pid(env, argv[1], &new_owner)) { + return enif_make_badarg(env); + } else if(!zlib_process_check(env, d)) { + return enif_raise_exception(env, am_not_on_controlling_process); + } + + enif_mutex_lock(d->controller_lock); + + d->controlling_process = new_owner; + + enif_mutex_unlock(d->controller_lock); return am_ok; } diff --git a/erts/preloaded/ebin/zlib.beam b/erts/preloaded/ebin/zlib.beam index 5048bdb846..f388bc723a 100644 Binary files a/erts/preloaded/ebin/zlib.beam and b/erts/preloaded/ebin/zlib.beam differ diff --git a/erts/preloaded/src/zlib.erl b/erts/preloaded/src/zlib.erl index 3170ab6351..03c9ae38a1 100644 --- a/erts/preloaded/src/zlib.erl +++ b/erts/preloaded/src/zlib.erl @@ -20,7 +20,8 @@ -module(zlib). --export([open/0,close/1,deflateInit/1,deflateInit/2,deflateInit/6, +-export([open/0,close/1,set_controlling_process/2, + deflateInit/1,deflateInit/2,deflateInit/6, deflateSetDictionary/2,deflateReset/1,deflateParams/3, deflate/2,deflate/3,deflateEnd/1, inflateInit/1,inflateInit/2,inflateInit/3, @@ -128,6 +129,14 @@ close(Z) -> close_nif(_Z) -> erlang:nif_error(undef). +-spec set_controlling_process(Z, Pid) -> 'ok' when + Z :: zstream(), + Pid :: pid(). +set_controlling_process(Z, Pid) -> + set_controller_nif(Z, Pid). +set_controller_nif(_Z, _Pid) -> + erlang:nif_error(undef). + -spec deflateInit(Z) -> 'ok' when Z :: zstream(). deflateInit(Z) -> diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index d17eded811..1afcd155b3 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -978,23 +978,37 @@ split_bin(Last,Acc) -> only_allow_owner(Config) when is_list(Config) -> Z = zlib:open(), + Owner = self(), ?m(ok, zlib:inflateInit(Z)), ?m(ok, zlib:inflateReset(Z)), {Pid, Ref} = spawn_monitor( fun() -> - ?m(?EXIT(not_on_controlling_process), zlib:inflateReset(Z)) + ?m(?EXIT(not_on_controlling_process), zlib:inflateReset(Z)), + Owner ! '$transfer_ownership', + receive + '$ownership_transferred' -> + ?m(ok, zlib:inflateReset(Z)) + after 200 -> + ct:fail("Never received transfer signal.") + end end), + ownership_transfer_check(Z, Pid, Ref). +ownership_transfer_check(Z, WorkerPid, Ref) -> receive - {'DOWN', Ref, process, Pid, _Reason} -> - ok + '$transfer_ownership' -> + zlib:set_controlling_process(Z, WorkerPid), + WorkerPid ! '$ownership_transferred', + ownership_transfer_check(Z, WorkerPid, Ref); + {'DOWN', Ref, process, WorkerPid, normal} -> + ok; + {'DOWN', Ref, process, WorkerPid, Reason} -> + ct:fail("Spawned worker crashed with reason ~p.", [Reason]) after 200 -> ct:fail("Spawned worker timed out.") - end, - - ?m(ok, zlib:inflateReset(Z)). + end. sub_heap_binaries(Config) when is_list(Config) -> Compressed = zlib:compress(<<"gurka">>), -- cgit v1.2.3