1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1997-2017. All Rights Reserved. 5%% 6%% Licensed under the Apache License, Version 2.0 (the "License"); 7%% you may not use this file except in compliance with the License. 8%% You may obtain a copy of the License at 9%% 10%% http://www.apache.org/licenses/LICENSE-2.0 11%% 12%% Unless required by applicable law or agreed to in writing, software 13%% distributed under the License is distributed on an "AS IS" BASIS, 14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15%% See the License for the specific language governing permissions and 16%% limitations under the License. 17%% 18%% %CopyrightEnd% 19%% 20 21-module(ddll_SUITE). 22 23%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 24%%% Checks if the dynamic driver and linker loader works. 25%%% 26%%% These tests can only be run installed (outside clearcase). 27%%% 28%%% XXX In this suite is missing test cases for reference counts 29%%% and that drivers are unloaded when their processes die. 30%%% (For me to add :-) 31%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 32 33 34-export([all/0, suite/0, 35 ddll_test/1, errors/1, reference_count/1, 36 kill_port/1, dont_kill_port/1]). 37-export([unload_on_process_exit/1, delayed_unload_with_ports/1, 38 unload_due_to_process_exit/1, 39 no_unload_due_to_process_exit/1, no_unload_due_to_process_exit_2/1, 40 unload_reload_thingie/1, unload_reload_thingie_2/1, 41 unload_reload_thingie_3/1, reload_pending/1, reload_pending_kill/1, 42 load_fail_init/1, 43 reload_pending_fail_init/1, 44 more_error_codes/1, forced_port_killing/1, 45 no_trap_exit_and_kill_ports/1, 46 monitor_demonitor/1, monitor_demonitor_load/1, new_interface/1, 47 lock_driver/1]). 48 49% Private exports 50-export([echo_loader/2, nice_echo_loader/2 ,properties/1, load_and_unload/1]). 51 52-import(ordsets, [subtract/2]). 53 54-include_lib("common_test/include/ct.hrl"). 55 56suite() -> 57 [{ct_hooks,[ts_install_cth]}, 58 {timetrap, {minutes, 1}}]. 59 60all() -> 61 [ddll_test, errors, reference_count, kill_port, 62 dont_kill_port, properties, load_and_unload, 63 unload_on_process_exit, delayed_unload_with_ports, 64 unload_due_to_process_exit, 65 no_unload_due_to_process_exit, 66 no_unload_due_to_process_exit_2, unload_reload_thingie, 67 unload_reload_thingie_2, unload_reload_thingie_3, 68 reload_pending, load_fail_init, 69 reload_pending_fail_init, reload_pending_kill, 70 more_error_codes, forced_port_killing, 71 no_trap_exit_and_kill_ports, monitor_demonitor, 72 monitor_demonitor_load, new_interface, lock_driver]. 73 74%% Check that the driver is unloaded on process exit 75unload_on_process_exit(Config) when is_list(Config) -> 76 Path = proplists:get_value(data_dir, Config), 77 false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), 78 Parent = self(), 79 Pid = spawn(fun() -> 80 receive go -> ok end, 81 erl_ddll:try_load(Path, echo_drv, []), 82 Parent ! gone, 83 receive go -> ok end, 84 erl_ddll:loaded_drivers(), 85 exit(banan) 86 end), 87 Ref = erlang:monitor(process,Pid), 88 false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), 89 Pid ! go, 90 receive 91 gone -> ok 92 end, 93 true = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), 94 Pid ! go, 95 receive 96 {'DOWN', Ref, process, Pid, banan} -> 97 ok 98 end, 99 receive after 500 -> ok end, 100 false = lists:member("echo_drv",element(2,erl_ddll:loaded_drivers())), 101 ok. 102 103%% Check that the driver is unloaded when the last port is closed 104delayed_unload_with_ports(Config) when is_list(Config) -> 105 Path = proplists:get_value(data_dir, Config), 106 erl_ddll:try_load(Path, echo_drv, []), 107 erl_ddll:try_load(Path, echo_drv, []), 108 Port = open_port({spawn, echo_drv}, [eof]), 109 1 = erl_ddll:info(echo_drv, port_count), 110 Port2 = open_port({spawn, echo_drv}, [eof]), 111 2 = erl_ddll:info(echo_drv, port_count), 112 {ok,pending_process} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]), 113 {ok,pending_driver,Ref} = erl_ddll:try_unload(echo_drv,[{monitor, pending_driver}]), 114 ok = receive _ -> false after 0 -> ok end, 115 Port ! {self(), close}, 116 ok = receive {Port,closed} -> ok after 1000 -> false end, 117 1 = erl_ddll:info(echo_drv, port_count), 118 Port2 ! {self(), close}, 119 ok = receive {Port2,closed} -> ok after 1000 -> false end, 120 ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 1000 -> false end, 121 ok. 122 123%% Check that the driver with ports is unloaded on process exit 124unload_due_to_process_exit(Config) when is_list(Config) -> 125 Path = proplists:get_value(data_dir, Config), 126 Parent = self(), 127 F3 = fun() -> 128 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 129 receive X -> Parent ! {got,X} end 130 end, 131 Pid = spawn(fun() -> 132 receive go -> ok end, 133 {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), 134 spawn(F3), 135 receive go -> ok end, 136 _Port = open_port({spawn, echo_drv}, [eof]), 137 _Port2 = open_port({spawn, echo_drv}, [eof]), 138 exit(banan) 139 end), 140 Ref = erlang:monitor(process,Pid), 141 Pid ! go, 142 {ok,Ref2} = receive 143 R when is_reference(R) -> {ok,R}; 144 Other -> {error, Other} 145 after 500 -> {error, timeout} 146 end, 147 Pid ! go, 148 ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, 149 ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, 150 ok. 151 152%% Check that a driver with driver loaded in another process is not unloaded on process exit 153no_unload_due_to_process_exit(Config) when is_list(Config) -> 154 Path = proplists:get_value(data_dir, Config), 155 Parent = self(), 156 F3 = fun() -> 157 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 158 receive X -> Parent ! {got,X} end 159 end, 160 Pid = spawn(fun() -> 161 receive go -> ok end, 162 {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), 163 spawn(F3), 164 receive go -> ok end, 165 _Port = open_port({spawn, echo_drv}, [eof]), 166 _Port2 = open_port({spawn, echo_drv}, [eof]), 167 exit(banan) 168 end), 169 Ref = erlang:monitor(process,Pid), 170 Pid ! go, 171 {ok,Ref2} = receive 172 R when is_reference(R) -> {ok,R}; 173 Other -> {error, Other} 174 after 500 -> {error, timeout} 175 end, 176 {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []), 177 Pid ! go, 178 ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, 179 ok = receive X -> {error, X} after 300 -> ok end, 180 ok = unload_expect_fast(echo_drv,[]), 181 ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, 182 ok. 183 184%% Check that a driver with open ports in another process is not unloaded on process exit 185no_unload_due_to_process_exit_2(Config) when is_list(Config) -> 186 Path = proplists:get_value(data_dir, Config), 187 Parent = self(), 188 F3 = fun() -> 189 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 190 receive X -> Parent ! {got,X} end 191 end, 192 Pid = spawn(fun() -> 193 receive go -> ok end, 194 {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), 195 spawn(F3), 196 receive go -> ok end, 197 _Port = open_port({spawn, echo_drv}, [eof]), 198 _Port2 = open_port({spawn, echo_drv}, [eof]), 199 exit(banan) 200 end), 201 Ref = erlang:monitor(process,Pid), 202 Pid ! go, 203 {ok,Ref2} = receive 204 R when is_reference(R) -> {ok,R}; 205 Other -> {error, Other} 206 after 500 -> {error, timeout} 207 end, 208 Port = open_port({spawn, echo_drv}, [eof]), 209 Pid ! go, 210 ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, 211 ok = receive X -> {error, X} after 300 -> ok end, 212 erlang:port_close(Port), 213 ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, 214 ok. 215 216%% Check delayed unload and reload 217unload_reload_thingie(Config) when is_list(Config) -> 218 Path = proplists:get_value(data_dir, Config), 219 Parent = self(), 220 {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), 221 F3 = fun() -> 222 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded_only}), 223 receive X -> Parent ! {got,X} end 224 end, 225 Pid = spawn(fun() -> 226 receive go -> ok end, 227 _Port = open_port({spawn, echo_drv}, [eof]), 228 spawn(F3), 229 receive go -> ok end, 230 exit(banan) 231 end), 232 Ref = erlang:monitor(process,Pid), 233 Pid ! go, 234 {ok,Ref2} = receive 235 R when is_reference(R) -> {ok,R}; 236 Other -> {error, Other} 237 after 500 -> {error, timeout} 238 end, 239 {ok,pending_driver,Ref3} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]), 240 Ref4 = erl_ddll:monitor(driver,{echo_drv,loaded}), 241 ok = receive {'DOWN',Ref4, driver,echo_drv,load_cancelled} -> ok after 1000 -> false end, 242 {ok,already_loaded} = erl_ddll:try_load(Path, echo_drv, []), 243 ok = receive {'UP',Ref3, driver,echo_drv,unload_cancelled} -> ok after 1000 -> false end, 244 Pid ! go, 245 ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, 246 [{Parent,1}] = erl_ddll:info(echo_drv, processes), 247 0 = erl_ddll:info(echo_drv, port_count), 248 ok = unload_expect_fast(echo_drv,[{monitor,pending}]), 249 ok = receive 250 {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok 251 after 300 -> error 252 end, 253 ok = receive X -> {error, X} after 300 -> ok end, 254 ok. 255 256%% Check delayed unload and reload 257unload_reload_thingie_2(Config) when is_list(Config) -> 258 Path = proplists:get_value(data_dir, Config), 259 Parent = self(), 260 {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), 261 F3 = fun() -> 262 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded_only}), 263 receive X -> Parent ! {got,X} end 264 end, 265 Pid = spawn(fun() -> 266 receive go -> ok end, 267 _Port = open_port({spawn, echo_drv}, [eof]), 268 spawn(F3), 269 receive go -> ok end, 270 exit(banan) 271 end), 272 Ref = erlang:monitor(process,Pid), 273 Pid ! go, 274 {ok,Ref2} = receive 275 R when is_reference(R) -> {ok,R}; 276 Other -> {error, Other} 277 after 500 -> {error, timeout} 278 end, 279 {ok,pending_driver,Ref3} = erl_ddll:try_load(Path, echo_drv, 280 [{monitor,pending_driver},{reload,pending_driver}]), 281 Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}), 282 Pid ! go, 283 ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, 284 ok = receive {'DOWN',Ref4, driver,echo_drv,unloaded} -> ok after 1000 -> false end, 285 ok = receive {'UP',Ref3, driver,echo_drv,loaded} -> ok after 1000 -> false end, 286 [{Parent,1}] = erl_ddll:info(echo_drv, processes), 287 0 = erl_ddll:info(echo_drv, port_count), 288 ok = receive 289 {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok 290 after 300 -> error 291 end, 292 ok = unload_expect_fast(echo_drv,[{monitor,pending}]), 293 ok = receive X -> {error, X} after 300 -> ok end, 294 ok. 295 296%% Check delayed unload and reload failure 297unload_reload_thingie_3(Config) when is_list(Config) -> 298 Path = proplists:get_value(data_dir, Config), 299 Parent = self(), 300 {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), 301 F3 = fun() -> 302 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 303 receive X -> Parent ! {got,X} end 304 end, 305 Pid = spawn(fun() -> 306 receive go -> ok end, 307 _Port = open_port({spawn, echo_drv}, [eof]), 308 spawn(F3), 309 receive go -> ok end, 310 exit(banan) 311 end), 312 Ref = erlang:monitor(process,Pid), 313 Pid ! go, 314 {ok,Ref2} = receive 315 R when is_reference(R) -> {ok,R}; 316 Other -> {error, Other} 317 after 500 -> {error, timeout} 318 end, 319 {ok,pending_driver,Ref3} = erl_ddll:try_load(filename:join([Path,"skrumpf"]), echo_drv, 320 [{monitor,pending_driver},{reload,pending_driver}]), 321 Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}), 322 Pid ! go, 323 ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, 324 ok = receive 325 {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok 326 after 300 -> error 327 end, 328 ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> false end, 329 ok = receive 330 {'DOWN',Ref3, driver,echo_drv,{load_failure,_}} -> ok 331 after 1000 -> false 332 end, 333 {'EXIT',_} = (catch erl_ddll:info(echo_drv, port_count)), 334 {error, not_loaded} = erl_ddll:try_unload(echo_drv,[{monitor,pending}]), 335 ok = receive X -> {error, X} after 300 -> ok end, 336 ok. 337 338%% Reload a driver that is pending on a user 339reload_pending(Config) when is_list(Config) -> 340 Path = proplists:get_value(data_dir, Config), 341 Parent = self(), 342 F3 = fun() -> 343 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 344 receive X -> Parent ! {got,X} end 345 end, 346 Pid = spawn(fun() -> 347 receive go -> ok end, 348 {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), 349 spawn(F3), 350 receive go -> ok end, 351 _Port = open_port({spawn, echo_drv}, [eof]), 352 _Port2 = open_port({spawn, echo_drv}, [eof]), 353 Parent ! opened, 354 receive go -> ok end, 355 exit(banan) 356 end), 357 Ref = erlang:monitor(process,Pid), 358 Pid ! go, 359 {ok,Ref2} = receive 360 R when is_reference(R) -> {ok,R}; 361 Other -> {error, Other} 362 after 500 -> {error, timeout} 363 end, 364 {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []), 365 Port = open_port({spawn, echo_drv}, [eof]), 366 Pid ! go, 367 receive opened -> ok end, 368 {error, pending_process} = 369 erl_ddll:try_load(Path, echo_drv, 370 [{reload,pending_driver}, 371 {monitor,pending_driver}]), 372 {ok, pending_process, Ref3} = 373 erl_ddll:try_load(Path, echo_drv, 374 [{reload,pending}, 375 {monitor,pending}]), 376 ok = receive X -> {error, X} after 300 -> ok end, 377 Pid ! go, 378 ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, 379 ok = receive Y -> {error, Y} after 300 -> ok end, 380 erlang:port_close(Port), 381 ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, 382 ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end, 383 [{Parent,1}] = erl_ddll:info(echo_drv,processes), 384 ok = receive Z -> {error, Z} after 300 -> ok end, 385 ok. 386 387%% Tests failure in the init in driver struct. 388load_fail_init(Config) when is_list(Config) -> 389 Path = proplists:get_value(data_dir, Config), 390 PathFailing = proplists:get_value(priv_dir, Config), 391 [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path), 392 lists:foreach(fun(Name) -> 393 Src = filename:join([Path,Name]), 394 Ext = filename:extension(Name), 395 Dst =filename:join([PathFailing,"echo_drv"++Ext]), 396 file:delete(Dst), 397 {ok,_} = file:copy(Src,Dst) 398 end, 399 AllFailInits), 400 [_|_] = filelib:wildcard("echo_drv.*",PathFailing), 401 {error, driver_init_failed} = erl_ddll:try_load(PathFailing, 402 echo_drv, 403 [{monitor,pending}]), 404 ok = receive XX -> 405 {unexpected,XX} 406 after 300 -> 407 ok 408 end, 409 ok. 410 411 412%% Reload a driver that is pending but init fails 413reload_pending_fail_init(Config) when is_list(Config) -> 414 Path = proplists:get_value(data_dir, Config), 415 PathFailing = proplists:get_value(priv_dir, Config), 416 [_|_] = AllFailInits = filelib:wildcard("echo_drv_fail_init.*",Path), 417 lists:foreach(fun(Name) -> 418 Src = filename:join([Path,Name]), 419 Ext = filename:extension(Name), 420 Dst =filename:join([PathFailing,"echo_drv"++Ext]), 421 file:delete(Dst), 422 {ok,_} = file:copy(Src,Dst) 423 end, 424 AllFailInits), 425 [_|_] = filelib:wildcard("echo_drv.*",PathFailing), 426 Parent = self(), 427 F3 = fun() -> 428 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 429 receive X -> Parent ! {got,X} end 430 end, 431 Pid = spawn(fun() -> 432 receive go -> ok end, 433 {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), 434 spawn(F3), 435 receive go -> ok end, 436 _Port = open_port({spawn, echo_drv}, [eof]), 437 _Port2 = open_port({spawn, echo_drv}, [eof]), 438 Parent ! opened, 439 receive go -> ok end, 440 exit(banan) 441 end), 442 Ref = erlang:monitor(process,Pid), 443 Pid ! go, 444 {ok,Ref2} = receive 445 R when is_reference(R) -> {ok,R}; 446 Other -> {error, Other} 447 after 500 -> {error, timeout} 448 end, 449 {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, []), 450 Port = open_port({spawn, echo_drv}, [eof]), 451 Pid ! go, 452 receive opened -> ok end, 453 {ok, pending_process, Ref3} = 454 erl_ddll:try_load(PathFailing, echo_drv, 455 [{reload,pending}, 456 {monitor,pending}]), 457 ok = receive X -> {error, X} after 300 -> ok end, 458 Pid ! go, 459 ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, 460 ok = receive Y -> {error, Y} after 300 -> ok end, 461 erlang:port_close(Port), 462 ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, 463 ok = receive {'DOWN', Ref3, driver, echo_drv, {load_failure,driver_init_failed}} -> ok after 300 -> error end, 464 {'EXIT',{badarg,_}} = (catch erl_ddll:info(echo_drv,processes)), 465 466 ok = receive Z -> {error, Z} after 300 -> ok end, 467 ok. 468 469%% Reload a driver with kill_ports option that is pending on a user 470reload_pending_kill(Config) when is_list(Config) -> 471 OldFlag = process_flag(trap_exit,true), 472 Path = proplists:get_value(data_dir, Config), 473 Parent = self(), 474 F3 = fun() -> 475 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 476 receive X -> Parent ! {got,X} end 477 end, 478 Pid = spawn(fun() -> 479 process_flag(trap_exit,true), 480 receive go -> ok end, 481 {ok, loaded} = erl_ddll:try_load(Path, echo_drv, [{driver_options,[kill_ports]}]), 482 spawn(F3), 483 receive go -> ok end, 484 Port = open_port({spawn, echo_drv}, [eof]), 485 Port2 = open_port({spawn, echo_drv}, [eof]), 486 Parent ! opened, 487 receive go -> ok end, 488 receive 489 {'EXIT', Port2, driver_unloaded} -> 490 Parent ! first_exit 491 end, 492 receive 493 {'EXIT', Port, driver_unloaded} -> 494 Parent ! second_exit 495 end, 496 receive go -> ok end, 497 exit(banan) 498 end), 499 Ref = erlang:monitor(process,Pid), 500 Pid ! go, 501 {ok,Ref2} = receive 502 R when is_reference(R) -> {ok,R}; 503 Other -> {error, Other} 504 after 500 -> {error, timeout} 505 end, 506 {ok, already_loaded} = erl_ddll:try_load(Path, echo_drv, [{driver_options,[kill_ports]}]), 507 {error,inconsistent} = erl_ddll:try_load(Path, echo_drv, []), 508 Port = open_port({spawn, echo_drv}, [eof]), 509 Pid ! go, 510 receive opened -> ok end, 511 {error, pending_process} = 512 erl_ddll:try_load(Path, echo_drv, 513 [{driver_options,[kill_ports]}, 514 {reload,pending_driver}, 515 {monitor,pending_driver}]), 516 {ok, pending_process, Ref3} = 517 erl_ddll:try_load(Path, echo_drv, 518 [{driver_options,[kill_ports]}, 519 {reload,pending}, 520 {monitor,pending}]), 521 ok = receive 522 {'EXIT', Port, driver_unloaded} -> 523 ok 524 after 300 -> error 525 end, 526 Pid ! go, 527 ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, 528 ok = receive {'UP', Ref3, driver, echo_drv, loaded} -> ok after 300 -> error end, 529 [_,_] = erl_ddll:info(echo_drv,processes), 530 ok = receive first_exit -> ok after 300 -> error end, 531 ok = receive second_exit -> ok after 300 -> error end, 532 0 = erl_ddll:info(echo_drv,port_count), 533 ok = receive X -> {error, X} after 300 -> ok end, 534 Pid ! go, 535 ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, 536 ok = receive Y -> {error, Y} after 300 -> ok end, 537 Port2 = open_port({spawn, echo_drv}, [eof]), 538 true = is_port(Port2), 539 [{Parent,1}] = erl_ddll:info(echo_drv,processes), 540 1 = erl_ddll:info(echo_drv,port_count), 541 erlang:port_close(Port2), 542 ok = receive {'EXIT', Port2, normal} -> ok after 300 -> error end, 543 0 = erl_ddll:info(echo_drv,port_count), 544 [{Parent,1}] = erl_ddll:info(echo_drv,processes), 545 Port3 = open_port({spawn, echo_drv}, [eof]), 546 {ok, pending_driver, Ref4} = 547 erl_ddll:try_unload(echo_drv,[{monitor,pending_driver}]), 548 ok = receive 549 {'EXIT', Port3, driver_unloaded} -> 550 ok 551 after 300 -> error 552 end, 553 ok = receive {'DOWN', Ref4, driver, echo_drv, unloaded} -> ok after 300 -> error end, 554 io:format("Port = ~w, Port2 = ~w, Port3 = ~w~n",[Port,Port2,Port3]), 555 ok = receive Z -> {error, Z} after 300 -> ok end, 556 process_flag(trap_exit,OldFlag), 557 ok. 558 559 560%% Some more error code checking 561more_error_codes(Config) when is_list(Config) -> 562 {error,Err} = erl_ddll:try_load("./echo_dr",echo_dr,[]), 563 true = is_list(erl_ddll:format_error(Err)), 564 true = is_list(erl_ddll:format_error(not_loaded)), 565 ok. 566 567%% Check kill_ports option to try_unload 568forced_port_killing(Config) when is_list(Config) -> 569 Path = proplists:get_value(data_dir, Config), 570 OldFlag=process_flag(trap_exit,true), 571 Parent = self(), 572 F3 = fun() -> 573 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 574 receive X -> Parent ! {got,X} end 575 end, 576 {ok, loaded} = erl_ddll:try_load(Path, echo_drv, []), 577 spawn(F3), 578 {ok,Ref2} = receive 579 R when is_reference(R) -> {ok,R}; 580 Other -> {error, Other} 581 after 500 -> {error, timeout} 582 end, 583 Port = open_port({spawn, echo_drv}, [eof]), 584 Port2 = open_port({spawn, echo_drv}, [eof]), 585 {ok, pending_driver, Ref1} = 586 erl_ddll:try_unload(echo_drv,[{monitor,pending_driver},kill_ports]), 587 ok = receive 588 {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok 589 after 300 -> error 590 end, 591 ok = receive {'EXIT',Port,driver_unloaded} -> ok after 300 -> false end, 592 ok = receive {'EXIT',Port2,driver_unloaded} -> ok after 300 -> false end, 593 ok = receive {'DOWN',Ref1, driver, echo_drv, unloaded} -> ok after 300 -> false end, 594 process_flag(trap_exit,OldFlag), 595 ok = receive X -> {error, X} after 300 -> ok end, 596 ok. 597 598%% Check delayed unload and reload with no trap_exit 599no_trap_exit_and_kill_ports(Config) when is_list(Config) -> 600 Path = proplists:get_value(data_dir, Config), 601 Parent = self(), 602 OldFlag=process_flag(trap_exit,true), 603 F3 = fun() -> 604 Parent ! erl_ddll:monitor(driver,{echo_drv,unloaded}), 605 receive X -> Parent ! {got,X} end 606 end, 607 Pid = spawn(fun() -> 608 process_flag(trap_exit,false), 609 receive go -> ok end, 610 {ok, loaded} = erl_ddll:try_load(Path, echo_drv, 611 [{driver_options,[kill_ports]}]), 612 spawn(F3), 613 receive go -> ok end, 614 _Port = open_port({spawn, echo_drv}, [eof]), 615 _Port2 = open_port({spawn, echo_drv}, [eof]), 616 exit(banan) 617 end), 618 Ref = erlang:monitor(process,Pid), 619 Pid ! go, 620 {ok,Ref2} = receive 621 R when is_reference(R) -> {ok,R}; 622 Other -> {error, Other} 623 after 500 -> {error, timeout} 624 end, 625 {error, inconsistent} = erl_ddll:try_load(Path, echo_drv, []), 626 MyPort = open_port({spawn, echo_drv}, [eof]), 627 Pid ! go, 628 ok = receive {'DOWN', Ref, process, Pid, banan} -> ok after 300 -> error end, 629 ok = receive {got,{'DOWN', Ref2, driver, echo_drv, unloaded}} -> ok after 300 -> error end, 630 ok = receive {'EXIT',MyPort,driver_unloaded} -> ok after 300 -> error end, 631 process_flag(trap_exit,OldFlag), 632 ok. 633 634%% Check monitor and demonitor of drivers 635monitor_demonitor(Config) when is_list(Config) -> 636 Path = proplists:get_value(data_dir, Config), 637 erl_ddll:try_load(Path, echo_drv, []), 638 Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}), 639 Self = self(), 640 [{Self,1}] = erl_ddll:info(echo_drv,awaiting_unload), 641 true = erl_ddll:demonitor(Ref), 642 [] = erl_ddll:info(echo_drv,awaiting_unload), 643 erl_ddll:try_unload(echo_drv,[]), 644 ok = receive _ -> error after 300 -> ok end, 645 ok. 646 647%% Check monitor/demonitor of driver loading 648monitor_demonitor_load(Config) when is_list(Config) -> 649 Path = proplists:get_value(data_dir, Config), 650 {ok,loaded} = erl_ddll:try_load(Path, echo_drv, []), 651 Port = open_port({spawn, echo_drv}, [eof]), 652 Ref = erl_ddll:monitor(driver,{echo_drv,loaded}), 653 ok = receive {'UP',Ref,driver,echo_drv,loaded} -> ok after 500 -> error end, 654 {ok, pending_driver} = erl_ddll:try_unload(echo_drv,[]), 655 Ref2 = erl_ddll:monitor(driver,{echo_drv,loaded}), 656 ok = receive {'DOWN',Ref2,driver,echo_drv,load_cancelled} -> ok after 0 -> error end, 657 {ok,already_loaded} = erl_ddll:try_load(Path, echo_drv, []), 658 {ok, pending_driver} = 659 erl_ddll:try_load(Path, echo_drv, [{reload,pending_driver}]), 660 Ref3 = erl_ddll:monitor(driver,{echo_drv,loaded}), 661 Ref4 = erl_ddll:monitor(driver,{echo_drv,unloaded}), 662 ok = receive _ -> error after 300 -> ok end, 663 Self = self(), 664 [{Self,1}] = erl_ddll:info(echo_drv,awaiting_load), 665 true = erl_ddll:demonitor(Ref3), 666 [] = erl_ddll:info(echo_drv,awaiting_load), 667 erlang:port_close(Port), 668 ok = receive {'DOWN',Ref4,driver,echo_drv,unloaded} -> ok after 300 -> error end, 669 ok = receive _ -> error after 300 -> ok end, 670 ok = unload_expect_fast(echo_drv,[]), 671 ok. 672 673%% Test the new load/unload/reload interface 674new_interface(Config) when is_list(Config) -> 675 Path = proplists:get_value(data_dir, Config), 676 % Typical scenario 677 ok = erl_ddll:load(Path, echo_drv), 678 Port = open_port({spawn, echo_drv}, [eof]), 679 ok = erl_ddll:unload(echo_drv), 680 Port ! {self(), {command, "text"}}, 681 ok = receive 682 {Port, {data, "text"}} -> ok; 683 _ -> error 684 after 685 1000 -> error 686 end, 687 Ref = erl_ddll:monitor(driver,{echo_drv,unloaded}), 688 ok = receive X -> {error, X} after 300 -> ok end, 689 erlang:port_close(Port), 690 ok = receive {'DOWN', Ref, driver, echo_drv, unloaded} -> ok after 300 -> error end, 691 % More than one user 692 ok = erl_ddll:load(Path, echo_drv), 693 Ref2 = erl_ddll:monitor(driver,{echo_drv,unloaded}), 694 ok = erl_ddll:load(Path, echo_drv), 695 ok = erl_ddll:load(Path, echo_drv), 696 Port2 = open_port({spawn, echo_drv}, [eof]), 697 ok = erl_ddll:unload(echo_drv), 698 Port2 ! {self(), {command, "text"}}, 699 ok = receive 700 {Port2, {data, "text"}} -> ok; 701 _ -> error 702 after 703 1000 -> error 704 end, 705 ok = erl_ddll:unload(echo_drv), 706 Port2 ! {self(), {command, "text"}}, 707 ok = receive 708 {Port2, {data, "text"}} -> ok; 709 _ -> error 710 after 711 1000 -> error 712 end, 713 ok = erl_ddll:unload(echo_drv), 714 Port2 ! {self(), {command, "text"}}, 715 ok = receive 716 {Port2, {data, "text"}} -> ok; 717 _ -> error 718 after 719 1000 -> error 720 end, 721 ok = receive X2 -> {error, X2} after 300 -> ok end, 722 ok = erl_ddll:load(Path, echo_drv), 723 ok = receive {'UP', Ref2, driver, echo_drv, unload_cancelled} -> ok after 300 -> error end, 724 Ref3 = erl_ddll:monitor(driver,{echo_drv,unloaded_only}), 725 erlang:port_close(Port2), 726 ok = receive X3 -> {error, X3} after 300 -> ok end, 727 ok = erl_ddll:unload(echo_drv), 728 ok = receive {'DOWN', Ref3, driver, echo_drv, unloaded} -> ok after 300 -> error end, 729 ok. 730 731 732 733 734ddll_test(Config) when is_list(Config) -> 735 Path = proplists:get_value(data_dir, Config), 736 737 %{error,{already_started,ErlDdllPid}} = erl_ddll:start(), 738 %ErlDdllPid = whereis(ddll_server), 739 740 %% Load the echo driver and verify that it was loaded. 741 {ok,L1,L2}=load_echo_driver(Path), 742 743 %% Verify that the driver works. 744 745 Port = open_port({spawn, echo_drv}, [eof]), 746 {hej, "hopp",4711,123445567436543653} = 747 erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}), 748 {hej, "hopp",4711,123445567436543653} = 749 erlang:port_call(Port,47,{hej, "hopp",4711,123445567436543653}), 750 Port ! {self(), {command, "text"}}, 751 1 = receive 752 {Port, {data, "text"}} -> 1; 753 _Other -> 2 754 after 755 1000 -> 2 756 end, 757 Port ! {self(), close}, 758 receive {Port, closed} -> ok end, 759 760 %% %% Unload the driver and verify that it was unloaded. 761 ok = unload_echo_driver(L1,L2), 762 763 %% %{error, {already_started, _}} = erl_ddll:start(), 764 ok. 765 766%% Tests errors having to do with bad drivers. 767 768errors(Config) when is_list(Config) -> 769 Path = proplists:get_value(data_dir, Config), 770 771 {ok, L1} = erl_ddll:loaded_drivers(), 772 773 {error, {open_error, _}} = erl_ddll:load_driver(Path, bad_name), 774 {error, driver_init_failed} = erl_ddll:load_driver(Path, initfail_drv), 775 {error, bad_driver_name} = erl_ddll:load_driver(Path, wrongname_drv), 776 777 %% We assume that there is a statically linked driver named "ddll": 778 {error, linked_in_driver} = erl_ddll:unload_driver(ram_file_drv), 779 {error, not_loaded} = erl_ddll:unload_driver("__pucko_driver__"), 780 781 case os:type() of 782 {unix, _} -> 783 {error, no_driver_init} = 784 erl_ddll:load_driver(Path, noinit_drv); 785 _ -> 786 ok 787 end, 788 789 {ok, L1} = erl_ddll:loaded_drivers(), 790 ok. 791 792%% Check that drivers are unloaded when their reference count 793%% reaches zero, and that they cannot be unloaded while 794%% they are still referenced. 795reference_count(Config) when is_list(Config) -> 796 Path = proplists:get_value(data_dir, Config), 797 798 %% Spawn a process that loads the driver (and holds a reference 799 %% to it). 800 Pid1=spawn_link(?MODULE, echo_loader, [Path, self()]), 801 receive 802 {Pid1, echo_loaded} -> ok 803 after 2000 -> ct:fail("echo_loader failed to start.") 804 end, 805 806 Pid1 ! {self(), die}, 807 test_server:sleep(200), % Give time to unload. 808 % Verify that the driver was automatically unloaded when the 809 % process died. 810 {error, not_loaded}=erl_ddll:unload_driver(echo_drv), 811 ok. 812 813% Loads the echo driver, send msg to started, sits and waits to 814% get a signal to die, then unloads the driver and terminates. 815echo_loader(Path, Starter) -> 816 {ok, L1, L2}=load_echo_driver(Path), 817 Starter ! {self(), echo_loaded}, 818 receive 819 {Starter, die} -> 820 unload_echo_driver(L1,L2) 821 end. 822 823% Loads the echo driver, send msg to started, sits and waits to 824% get a signal to die, then unloads the driver and terminates. 825nice_echo_loader(Path, Starter) -> 826 {ok, L1, L2}=load_nice_echo_driver(Path), 827 Starter ! {self(), echo_loaded}, 828 receive 829 {Starter, die} -> 830 unload_echo_driver(L1,L2) 831 end. 832 833 834%% Test that a port that uses a driver is killed when the 835%% process that loaded the driver dies. 836kill_port(Config) when is_list(Config) -> 837 Path = proplists:get_value(data_dir, Config), 838 839 %% Spawn a process that loads the driver (and holds a reference 840 %% to it). 841 Pid1=spawn(?MODULE, echo_loader, [Path, self()]), 842 receive 843 {Pid1, echo_loaded} -> 844 ok 845 after 3000 -> 846 exit(Pid1, kill), 847 ct:fail("echo_loader failed to start.") 848 end, 849 850 % Spawn off a port that uses the driver. 851 Port = open_port({spawn, echo_drv}, [eof]), 852 853 % Kill the process / unload the driver. 854 process_flag(trap_exit, true), 855 exit(Pid1, kill), 856 test_server:sleep(200), % Give some time to unload. 857 {error, not_loaded} = erl_ddll:unload_driver(echo_drv), 858 859 % See if the port is killed. 860 receive 861 {'EXIT', Port, Reason} -> 862 io:format("Port exited with reason ~w", [Reason]) 863 after 5000 -> 864 ct:fail("Echo port did not terminate.") 865 end, 866 ok. 867 868%% Test that a port that uses a driver is not killed when the 869%% process that loaded the driver dies and it's nicely opened. 870dont_kill_port(Config) when is_list(Config) -> 871 Path = proplists:get_value(data_dir, Config), 872 873 %% Spawn a process that loads the driver (and holds a reference 874 %% to it). 875 Pid1=spawn(?MODULE, nice_echo_loader, [Path, self()]), 876 receive 877 {Pid1, echo_loaded} -> 878 ok 879 after 3000 -> 880 exit(Pid1, kill), 881 ct:fail("echo_loader failed to start.") 882 end, 883 884 % Spawn off a port that uses the driver. 885 Port = open_port({spawn, echo_drv}, [eof]), 886 887 % Kill the process / unload the driver. 888 process_flag(trap_exit, true), 889 exit(Pid1, kill), 890 test_server:sleep(200), % Give some time to unload. 891 {hej, "hopp",4711,123445567436543653} = 892 erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}), 893 [] = erl_ddll:info(echo_drv,processes), 894 %% unload should work with no owner 895 ok = erl_ddll:unload_driver(echo_drv), %Kill ports while at it 896 897 % See if the port is killed. 898 receive 899 {'EXIT', Port, Reason} -> 900 io:format("Port exited with reason ~w", [Reason]) 901 after 5000 -> 902 ct:fail("Echo port did not terminate.") 903 end, 904 ok. 905 906%% Test that a process that loaded a driver 907%% is the only process that can unload it. 908properties(Config) when is_list(Config) -> 909 Path = proplists:get_value(data_dir, Config), 910 911 % Let another process load the echo driver. 912 Pid=spawn_link(?MODULE, echo_loader, [Path, self()]), 913 receive 914 {Pid, echo_loaded} -> ok 915 after 2000 -> ct:fail("echo_loader failed to start.") 916 end, 917 918 % Try to unload the driver from this process (the wrong one). 919 {error, _} = erl_ddll:unload_driver(echo_drv), 920 {ok, Drivers} = erl_ddll:loaded_drivers(), 921 case lists:member("echo_drv", Drivers) of 922 true -> 923 ok; 924 false -> 925 ct:fail("Unload from wrong process succeeded.") 926 end, 927 928 % Unload the driver and terminate dummy process. 929 Pid ! {self(), die}, 930 test_server:sleep(200), % Give time to unload. 931 ok. 932 933%% Load two drivers and unload them in load order. 934load_and_unload(Config) when is_list(Config) -> 935 Path = proplists:get_value(data_dir, Config), 936 {ok, Loaded_drivers1} = erl_ddll:loaded_drivers(), 937 ok = erl_ddll:load_driver(Path, echo_drv), 938 ok = erl_ddll:load_driver(Path, dummy_drv), 939 ok = erl_ddll:unload_driver(echo_drv), 940 ok = erl_ddll:unload_driver(dummy_drv), 941 {ok, Loaded_drivers2} = erl_ddll:loaded_drivers(), 942 Set1 = ordsets:from_list(Loaded_drivers1), 943 Set2 = ordsets:from_list(Loaded_drivers2), 944 io:format("~p == ~p\n", [Loaded_drivers1, Loaded_drivers2]), 945 [] = ordsets:to_list(ordsets:subtract(Set2, Set1)), 946 ok. 947 948%% Check multiple calls to driver_lock_driver 949lock_driver(Config) when is_list(Config) -> 950 Path = proplists:get_value(data_dir, Config), 951 {ok, _} = erl_ddll:try_load(Path, lock_drv, []), 952 Port1 = open_port({spawn, lock_drv}, [eof]), 953 Port2 = open_port({spawn, lock_drv}, [eof]), 954 true = erl_ddll:info(lock_drv,permanent), 955 erlang:port_close(Port1), 956 erlang:port_close(Port2), 957 ok. 958 959 960% Load and unload the echo_drv driver. 961% Make sure that the driver doesn't exist before we load it, 962% and that it exists before we unload it. 963load_echo_driver(Path) -> 964 {ok, L1} = erl_ddll:loaded_drivers(), 965 ok = erl_ddll:load_driver(Path, echo_drv), 966 {ok, L2} = erl_ddll:loaded_drivers(), 967 ["echo_drv"] = ordsets:to_list(subtract(ordsets:from_list(L2), 968 ordsets:from_list(L1))), 969 {ok,L1,L2}. 970 971load_nice_echo_driver(Path) -> 972 {ok, L1} = erl_ddll:loaded_drivers(), 973 ok = erl_ddll:load(Path, echo_drv), 974 {ok, L2} = erl_ddll:loaded_drivers(), 975 ["echo_drv"] = ordsets:to_list(subtract(ordsets:from_list(L2), 976 ordsets:from_list(L1))), 977 {ok,L1,L2}. 978 979unload_echo_driver(L1,L2) -> 980 {ok, L2} = erl_ddll:loaded_drivers(), 981 ok = erl_ddll:unload_driver(echo_drv), 982 {ok, L3} = erl_ddll:loaded_drivers(), 983 [] = ordsets:to_list(subtract(ordsets:from_list(L3), 984 ordsets:from_list(L1))), 985 ok. 986 987unload_expect_fast(Driver,XFlags) -> 988 {ok, pending_driver, Ref} = 989 erl_ddll:try_unload(Driver, 990 [{monitor,pending_driver}]++XFlags), 991 receive 992 {'DOWN', Ref, driver, Driver, unloaded} -> 993 case lists:member(atom_to_list(Driver),element(2,erl_ddll:loaded_drivers())) of 994 true -> 995 {error, {still_there, Driver}}; 996 false -> 997 ok 998 end 999 after 1000 -> 1000 {error,{unable_to_unload, Driver}} 1001 end. 1002