aboutsummaryrefslogblamecommitdiffstats
path: root/erts/emulator/test/nif_SUITE_data/nif_SUITE.c
blob: 6cf02c6efec738553452e6fb491a1cbf9be4e65e (plain) (tree)
1
2
3
4
5
6
7
8
9
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780


                   
                                                        
  


                                                                   
  






                                                                           


                 
                    

                  

                   
                   

                   

                  
      


                    




















                                                                                                                          


                                                 
                               
                              



                                              



                                     


                                
                                          





                                               
                              
 




                            
                                                         











                                                                     


                                                                    
                                                                    





                                       

                     

 
                                                                        
 








                                                    
                                                      



                                                                     









                                                            

                                                        
                                                                          





                                            
                     
                  

  




















                                                                                     
 

                                                                    

                                                 
            
                                





                                                         

                     


                                                                      

 

                                                                         
                                                  




                              

                   
                                
 
                                                                              
                                                                        
                                                                                

                                                                        






                                                                                


                                                                           






                                                                                     
                                             
                                           



                                                                           



                                                         


                                                
                                                                    





                                                                              
                                           
 



                      



























                                                                                      

                                                                                                  




                                                







                                                   



                                                  
     
                   

 
                                                                                    










                                                                       


                                                                     



                                                                     




                                                                                    

                                                   
                        



                
                                                                                     
 
                                                     



                                                      
                                                                                               
 
                                                     
              
    
                                         

                                     

                                              
     
                                              


                                                          
                                                                                             
 
                                                     
                     
                                
                                                                               
     



                                                               

 
                                                                                 


                      
                                          









                                                                    










                                                                    





















                                                                       




                                                         
                                                                        




                 























                                                               




                                                      
                                                                       






                                                    



                                                   

                                                  
      



                                                                                  



                        

                        
             
                                  
 
                   
        
                                  

                       


                              
        
                                  

                       

                             
 














                                    













                                                         

                    
              
                                   
                       
         





                                     
                       


                               
         







                                               











                                             




                                              


                                                           

                                                                          


                                                                            

                       





                                                                            
     





                                                          

                                                                        
                                                      


                       
                                                                                           
                                                       


                       
 


                                                         
                                                                        

                                             






                                       






































                                                                                                        


                                                                                     
                            















                                                                                     
                                                                    







                                                                                
                                                             

 













                                                                                      
 



                                                                                  



                                                           




















                                                                                      
                                            












                                                                                    
                                            

                                     
                                                                            


























































                                                                                                

                                     


                                                                      
                                   
                                                                      
                                      












                                                                                          
                                                     




                                                      
                                                  




                                                                                       


                                                    
                                                        
                                                                       


                                     

                                               



                                                                                      

                                            

                                     
                                         




                                                                                          
                                                    

                     
                                            
                                                        
                                                                       




                                               
                                


               


                                                                                                 
                                                      














                                                                                   
                                                               










                                                               


                                                                                     

                                                    
 



                                                                


                                                                      
          
                                            

                       
                                                             

                                     


                                                                 




                                                                                         

                                            

                                     
                                


                                    










                           
                                                  







                                                                                 
                                                                           






                                                                        
                                                                     


                   
 
  



                                                                            
                                      


                                                                                           
                                  
                                                           






                                                             



                  




                                
                        




                                                                                    
                                                                              







                                                              
                                                                 




                                                 


                                                 


                                     












                                                                                   
                                                                                  


                                            
                                                                                       


                                            
                                                                                        










                                                                                     
                                                                                                                                                                                                                   
 
                                


                                                                                     

                                                                                     
 








































                                                                                      
                      




























                                                                    
                                       




















































                                                                              





























                                                                           
                      
                           


                                                      





























































                                                                                        









                                                                 



















                                                  

                                                                            
                                  
                                   
                                     














                                                                        
                          







































                                                                                      
                           

























                                                                                     
              



                                                                          
                             







                                                                                  
              
                      

                                                                       



                                     

                                          





                                                                                  
              








                                                                       

                                 


                                                                        

                                                                                   
              
                 








                                                                           

                                 


                                  


                                
              








                                                                                   

                                 




                                                                                         
              
























                                                                                         
              













                                                                                   

                                                                                  
              


















                                                                                  
 

                                                                                       








                                                  


                                                                                       
                                                        



                                             



















                                                                                     



















                                                                                     



                                                                                              















                                                                           



















                                                                                          
                        

                                            


                                                                             

 
  





                                                                           


                                                                                           

                                                             














                                                                     

 






                                       












                                                                                            
                         





                                                                               
                         



                                        
                                                  































                                                                                               





                                                                                         
                               
                                                     
                                                                                         






























                                                                                            
          
                                                                                           




















                                                                                        
                                                                                                    



                                                           



                                    

                                            
                                            
 
                                                                                
                                            
 
            
                 
                                                                
                      
                                                
                                                                                     

                                                       
     

                                            
 
                                                                               
                                            
 
            
                 
                                                                
                      







                                                               
 
                                                                                     
                                                       
              










                                                               

     

                                            



                                                 







                                                                                       
                                                
                                
                                                          
                                 
                                                          
                                 
                                                         













                                                                                    
                                                
                                
                                                          
                                 
                                                          
                                 
                                                         



















                                                                                          
                                                
                           
                                                          
                            
                                                          
                            
                                                         



                                            
                                                
                         
                                                          
                          
                                                          
                          
                                                         






                                                                       





























                                                                                       



















                                                                                         


























                                                                                       
                                

                             

                      



                                               

                                                

                                     



                                                                 
 
                                          


                                            
                        
            
                                                     


     











                                                                                     
















                                                                                    
                                                                               
 
                                                                        

                 




                                                                                   
                            

                                
                                  
                     
                                   
 
                                   





                                                                    





                                                       
 
                          
                              
                                                            
 
                                           






                                                                                 
















                                                                                   
                                
                            
                                 




                                                   


                                                                               



                                                                                  
                            


                       
                                   



                                                    
                                                                   



















                                                                                   
                            
                       
                 

                     
                                   





                                                            
                                      






















                                                                                      
                            
 
                                    

                                     
                                                



                                                       
                                                       
                            



                                   

 





                        

                                                                       
 


                                                       




                                                  


                                         











                                                                       

 








                                                                                          

 

















































































































































































































































































































































































































































































                                                                                                          





                                                          



                                      
                          













                                                            
                                                
                               
                                                  
                                    
                                  







                                                              
                                
                                
                                  
                                              

                                              
                                
                                          
                                     
                              
                                  
                                      
                                      
                                                        
                                                
                                                  

                                                          







                                                    


                                                                


                                                

                                                  

                                              
                                              
                                          
                                        
                                  


                                
                                        




                                                                  

  
                                                          
/*
 * %CopyrightBegin%
 *
 * Copyright Ericsson AB 2009-2016. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * %CopyrightEnd%
 */
#include <erl_nif.h>

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <limits.h>
#ifndef __WIN32__
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#endif

#include "nif_mod.h"

#if 0
static ErlNifMutex* dbg_trace_lock;
#define DBG_TRACE_INIT dbg_trace_lock = enif_mutex_create("nif_SUITE.DBG_TRACE")
#define DBG_TRACE_FINI enif_mutex_destroy(dbg_trace_lock)
#define DBG_TRACE_LOCK enif_mutex_lock(dbg_trace_lock)
#define DBG_TRACE_UNLOCK enif_mutex_unlock(dbg_trace_lock)
#define DBG_TRACE0(FMT) do {DBG_TRACE_LOCK; enif_fprintf(stderr, FMT); DBG_TRACE_UNLOCK; }while(0)
#define DBG_TRACE1(FMT, A) do {DBG_TRACE_LOCK; enif_fprintf(stderr, FMT, A); DBG_TRACE_UNLOCK; }while(0)
#define DBG_TRACE2(FMT, A, B) do {DBG_TRACE_LOCK; enif_fprintf(stderr, FMT, A, B); DBG_TRACE_UNLOCK; }while(0)
#define DBG_TRACE3(FMT, A, B, C) do {DBG_TRACE_LOCK; enif_fprintf(stderr, FMT, A, B, C); DBG_TRACE_UNLOCK; }while(0)
#define DBG_TRACE4(FMT, A, B, C, D) do {DBG_TRACE_LOCK; enif_fprintf(stderr, FMT, A, B, C, D); DBG_TRACE_UNLOCK; }while(0)
#else
#define DBG_TRACE_INIT
#define DBG_TRACE_FINI
#define DBG_TRACE0(FMT)
#define DBG_TRACE1(FMT, A)
#define DBG_TRACE2(FMT, A, B)
#define DBG_TRACE3(FMT, A, B, C)
#define DBG_TRACE4(FMT, A, B, C, D)
#endif

static int static_cntA; /* zero by default */
static int static_cntB = NIF_SUITE_LIB_VER * 100;

static ERL_NIF_TERM atom_false;
static ERL_NIF_TERM atom_true;
static ERL_NIF_TERM atom_self;
static ERL_NIF_TERM atom_ok;
static ERL_NIF_TERM atom_join;
static ERL_NIF_TERM atom_binary_resource_type;
static ERL_NIF_TERM atom_second;
static ERL_NIF_TERM atom_millisecond;
static ERL_NIF_TERM atom_microsecond;
static ERL_NIF_TERM atom_nanosecond;
static ERL_NIF_TERM atom_eagain;
static ERL_NIF_TERM atom_eof;
static ERL_NIF_TERM atom_error;
static ERL_NIF_TERM atom_fd_resource_stop;
static ERL_NIF_TERM atom_monitor_resource_type;
static ERL_NIF_TERM atom_monitor_resource_down;
static ERL_NIF_TERM atom_init;
static ERL_NIF_TERM atom_stats;
static ERL_NIF_TERM atom_done;
static ERL_NIF_TERM atom_stop;
static ERL_NIF_TERM atom_null;

typedef struct
{
    int ref_cnt;
    CallInfo* call_history;
    NifModPrivData* nif_mod;
    union { ErlNifResourceType* t; void* vp; } rt_arr[2];
} PrivData;

/*
 * Use a union for pointer type conversion to avoid compiler warnings
 * about strict-aliasing violations with gcc-4.1. gcc >= 4.2 does not
 * emit the warning.
 * TODO: Reconsider use of union once gcc-4.1 is obsolete?
 */
typedef union {
    void* vp;
    struct make_term_info* p;
} mti_t;

void add_call(ErlNifEnv* env, PrivData* data, const char* func_name)
{
    CallInfo* call = enif_alloc(sizeof(CallInfo)+strlen(func_name));
    strcpy(call->func_name, func_name);
    call->lib_ver = NIF_SUITE_LIB_VER;
    call->next = data->call_history;
    call->static_cntA = ++static_cntA;
    call->static_cntB = ++static_cntB;
    data->call_history = call;
    call->arg = NULL;
    call->arg_sz = 0;
}

#define ADD_CALL(FUNC_NAME) add_call(env, enif_priv_data(env),FUNC_NAME)

static void*    resource_dtor_last = NULL;
static unsigned resource_dtor_last_sz = 0;
static char     resource_dtor_last_data[20];
static int resource_dtor_cnt = 0;

static void resource_dtor(ErlNifEnv* env, void* obj)
{
    resource_dtor_last = obj;
    resource_dtor_cnt++;
    resource_dtor_last_sz = enif_sizeof_resource(obj);
    assert(resource_dtor_last_sz <= sizeof(resource_dtor_last_data));
    memcpy(resource_dtor_last_data, obj, resource_dtor_last_sz);
}

static ErlNifResourceType* msgenv_resource_type;
static void msgenv_dtor(ErlNifEnv* env, void* obj);

static ErlNifResourceType* binary_resource_type;
static void binary_resource_dtor(ErlNifEnv* env, void* obj);
struct binary_resource {
    unsigned char* data;
    unsigned size;
};

static ErlNifResourceType* fd_resource_type;
static void fd_resource_dtor(ErlNifEnv* env, void* obj);
static void fd_resource_stop(ErlNifEnv* env, void* obj, ErlNifEvent, int);
static ErlNifResourceTypeInit fd_rt_init = {
    fd_resource_dtor,
    fd_resource_stop
};
struct fd_resource {
    int fd;
    int was_selected;
    ErlNifPid pid;
};

static ErlNifResourceType* monitor_resource_type;
static void monitor_resource_dtor(ErlNifEnv* env, void* obj);
static void monitor_resource_down(ErlNifEnv*, void* obj, ErlNifPid*, ErlNifMonitor*);
static ErlNifResourceTypeInit monitor_rt_init = {
    monitor_resource_dtor,
    NULL,
    monitor_resource_down
};
struct monitor_resource {
    ErlNifPid receiver;
    int use_msgenv;
};

static ErlNifResourceType* frenzy_resource_type;
static void frenzy_resource_dtor(ErlNifEnv* env, void* obj);
static void frenzy_resource_down(ErlNifEnv*, void* obj, ErlNifPid*, ErlNifMonitor*);
static ErlNifResourceTypeInit frenzy_rt_init = {
    frenzy_resource_dtor,
    NULL,
    frenzy_resource_down
};

static int get_pointer(ErlNifEnv* env, ERL_NIF_TERM term, void** pp)
{
    ErlNifBinary bin;
    int r = enif_inspect_binary(env, term, &bin);
    if (r) {
	*pp = *(void**)bin.data;
    }
    return r;
}

static ERL_NIF_TERM make_pointer(ErlNifEnv* env, void* p)
{
    void** bin_data;
    ERL_NIF_TERM res;
    bin_data = (void**)enif_make_new_binary(env, sizeof(void*), &res);
    *bin_data = p;
    return res;
}

static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
{
    PrivData* data = enif_alloc(sizeof(PrivData));
    assert(data != NULL);
    data->ref_cnt = 1;
    data->call_history = NULL;
    data->nif_mod = NULL;

    DBG_TRACE_INIT;

    add_call(env, data, "load");

    data->rt_arr[0].t = enif_open_resource_type(env,NULL,"Gold",resource_dtor,
						ERL_NIF_RT_CREATE,NULL);
    data->rt_arr[1].t = enif_open_resource_type(env,NULL,"Silver",resource_dtor,
						ERL_NIF_RT_CREATE,NULL);

    binary_resource_type =  enif_open_resource_type(env,NULL,"nif_SUITE.binary",
						    binary_resource_dtor,
						    ERL_NIF_RT_CREATE, NULL);

    msgenv_resource_type =  enif_open_resource_type(env,NULL,"nif_SUITE.msgenv",
						    msgenv_dtor,
						    ERL_NIF_RT_CREATE, NULL);
    fd_resource_type =  enif_open_resource_type_x(env, "nif_SUITE.fd",
                                                  &fd_rt_init,
                                                  ERL_NIF_RT_CREATE, NULL);
    monitor_resource_type = enif_open_resource_type_x(env, "nif_SUITE.monitor",
                                                      &monitor_rt_init,
                                                      ERL_NIF_RT_CREATE, NULL);
    frenzy_resource_type = enif_open_resource_type_x(env, "nif_SUITE.monitor_frenzy",
						      &frenzy_rt_init,
						      ERL_NIF_RT_CREATE, NULL);

    atom_false = enif_make_atom(env,"false");
    atom_true = enif_make_atom(env,"true");
    atom_self = enif_make_atom(env,"self");
    atom_ok = enif_make_atom(env,"ok");
    atom_join = enif_make_atom(env,"join");
    atom_binary_resource_type = enif_make_atom(env,"binary_resource_type");
    atom_second = enif_make_atom(env,"second");
    atom_millisecond = enif_make_atom(env,"millisecond");
    atom_microsecond = enif_make_atom(env,"microsecond");
    atom_nanosecond = enif_make_atom(env,"nanosecond");
    atom_eagain = enif_make_atom(env, "eagain");
    atom_eof = enif_make_atom(env, "eof");
    atom_error = enif_make_atom(env, "error");
    atom_fd_resource_stop = enif_make_atom(env, "fd_resource_stop");
    atom_monitor_resource_type = enif_make_atom(env, "monitor_resource_type");
    atom_monitor_resource_down = enif_make_atom(env, "monitor_resource_down");
    atom_init = enif_make_atom(env,"init");
    atom_stats = enif_make_atom(env,"stats");
    atom_done = enif_make_atom(env,"done");
    atom_stop = enif_make_atom(env,"stop");
    atom_null = enif_make_atom(env,"null");

    *priv_data = data;
    return 0;
}

static void resource_takeover(ErlNifEnv* env, PrivData* priv)
{
    ErlNifResourceFlags tried;
    ErlNifResourceType* rt;
    rt = enif_open_resource_type(env, NULL, "Gold", resource_dtor,
				 ERL_NIF_RT_TAKEOVER, &tried);
    assert(rt == priv->rt_arr[0].t); 
    assert(tried == ERL_NIF_RT_TAKEOVER);
    rt = enif_open_resource_type(env, NULL, "Silver", resource_dtor,
				 ERL_NIF_RT_TAKEOVER, &tried);
    assert(rt == priv->rt_arr[1].t); 
    assert(tried == ERL_NIF_RT_TAKEOVER);

    rt =  enif_open_resource_type(env, NULL, "nif_SUITE.binary", binary_resource_dtor,
				  ERL_NIF_RT_TAKEOVER, &tried);
    assert(rt != NULL);
    assert(tried == ERL_NIF_RT_TAKEOVER);
    assert(binary_resource_type==NULL || binary_resource_type == rt); 
    binary_resource_type = rt;

    rt = enif_open_resource_type(env, NULL, "nif_SUITE.msgenv", msgenv_dtor,
				  ERL_NIF_RT_TAKEOVER, &tried);
    assert(rt != NULL);
    assert(tried == ERL_NIF_RT_TAKEOVER);
    assert(msgenv_resource_type==NULL || msgenv_resource_type == rt); 
    msgenv_resource_type = rt;
}

static int upgrade(ErlNifEnv* env, void** priv_data, void** old_priv_data, ERL_NIF_TERM load_info)
{
    PrivData* priv = (PrivData*) *old_priv_data;
    add_call(env, priv, "upgrade");
    priv->ref_cnt++;
    *priv_data = *old_priv_data;
    resource_takeover(env,priv);
    return 0;
}

static void unload(ErlNifEnv* env, void* priv_data)
{
    PrivData* data = priv_data;
    add_call(env, data, "unload");
    if (--data->ref_cnt == 0) {
	if (data->nif_mod != NULL) {
	    NifModPrivData_release(data->nif_mod);
	}
	enif_free(priv_data);
    }
    DBG_TRACE_FINI;
}

static ERL_NIF_TERM lib_version(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ADD_CALL("lib_version");
    return enif_make_int(env, NIF_SUITE_LIB_VER);
}

static ERL_NIF_TERM make_call_history(ErlNifEnv* env, CallInfo** headp)
{
    ERL_NIF_TERM list = enif_make_list(env, 0); /* NIL */

    while (*headp != NULL) {
	CallInfo* call = *headp;
	ERL_NIF_TERM func_term = enif_make_atom(env,call->func_name);
	ERL_NIF_TERM tpl;
	if (call->arg != NULL) {
	    ERL_NIF_TERM arg_bin;	    
	    memcpy(enif_make_new_binary(env, call->arg_sz, &arg_bin),
		   call->arg, call->arg_sz);
	    func_term = enif_make_tuple2(env, func_term, arg_bin);
	}
	tpl = enif_make_tuple4(env, func_term, 					    
			       enif_make_int(env,call->lib_ver),
			       enif_make_int(env,call->static_cntA),
			       enif_make_int(env,call->static_cntB));
	list = enif_make_list_cell(env, tpl, list);
	*headp = call->next;
	enif_free(call);
    }
    return list;
}

static ERL_NIF_TERM call_history(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    PrivData* data = (PrivData*) enif_priv_data(env);

    return make_call_history(env,&data->call_history);
}

static ERL_NIF_TERM hold_nif_mod_priv_data(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    PrivData* data = (PrivData*) enif_priv_data(env);
    void* ptr;
    
    if (!get_pointer(env,argv[0],&ptr)) {
	return enif_make_badarg(env);
    }
    if (data->nif_mod != NULL) {
	NifModPrivData_release(data->nif_mod);
    }
    data->nif_mod = (NifModPrivData*) ptr;    
    return enif_make_int(env,++(data->nif_mod->ref_cnt)); 
}

static ERL_NIF_TERM nif_mod_call_history(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    PrivData* data = (PrivData*) enif_priv_data(env);
    ERL_NIF_TERM ret;
    if (data->nif_mod == NULL) {
	return enif_make_string(env,"nif_mod pointer is NULL", ERL_NIF_LATIN1);
    }
    enif_mutex_lock(data->nif_mod->mtx);
    ret = make_call_history(env, &data->nif_mod->call_history);
    enif_mutex_unlock(data->nif_mod->mtx);
    return ret;
}

static ERL_NIF_TERM list_seq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM list;
    int n;
    if (!enif_get_int(env, argv[0], &n)) {
	return enif_make_badarg(env);
    }
    list = enif_make_list(env, 0); /* NIL */
    while (n > 0) {
	list = enif_make_list_cell(env, enif_make_int(env,n), list);
	n--;
    }
    return list;
}

static int test_int(ErlNifEnv* env, int i1)
{
    int i2 = 0;
    ERL_NIF_TERM int_term = enif_make_int(env, i1);
    if (!enif_get_int(env,int_term, &i2) || i1 != i2) {
	fprintf(stderr, "test_int(%d) ...FAILED i2=%d\r\n", i1, i2);
	return 0;
    }
    return 1;
}

static int test_uint(ErlNifEnv* env, unsigned i1)
{
    unsigned i2 = 0;
    ERL_NIF_TERM int_term = enif_make_uint(env, i1);
    if (!enif_get_uint(env,int_term, &i2) || i1 != i2) {
	fprintf(stderr, "test_uint(%u) ...FAILED i2=%u\r\n", i1, i2);
	return 0;
    }
    return 1;
}

static int test_long(ErlNifEnv* env, long i1)
{
    long i2 = 0;
    ERL_NIF_TERM int_term = enif_make_long(env, i1);
    if (!enif_get_long(env,int_term, &i2) || i1 != i2) {
	fprintf(stderr, "test_long(%ld) ...FAILED i2=%ld\r\n", i1, i2);
	return 0;
    }
    return 1;
}

static int test_ulong(ErlNifEnv* env, unsigned long i1)
{
    unsigned long i2 = 0;
    ERL_NIF_TERM int_term = enif_make_ulong(env, i1);
    if (!enif_get_ulong(env,int_term, &i2) || i1 != i2) {
	fprintf(stderr, "test_ulong(%lu) ...FAILED i2=%lu\r\n", i1, i2);
	return 0;
    }
    return 1;
}

static int test_int64(ErlNifEnv* env, ErlNifSInt64 i1)
{
    ErlNifSInt64 i2 = 0;
    ERL_NIF_TERM int_term = enif_make_int64(env, i1);
    if (!enif_get_int64(env,int_term, &i2) || i1 != i2) {
	fprintf(stderr, "test_int64(%ld) ...FAILED i2=%ld\r\n",
		(long)i1, (long)i2);
	return 0;
    }
    return 1;
}

static int test_uint64(ErlNifEnv* env, ErlNifUInt64 i1)
{
    ErlNifUInt64 i2 = 0;
    ERL_NIF_TERM int_term = enif_make_uint64(env, i1);
    if (!enif_get_uint64(env,int_term, &i2) || i1 != i2) {
	fprintf(stderr, "test_ulong(%lu) ...FAILED i2=%lu\r\n",
		(unsigned long)i1, (unsigned long)i2);
	return 0;
    }
    return 1;
}

static int test_double(ErlNifEnv* env, double d1)
{
    double d2 = 0;
    ERL_NIF_TERM term = enif_make_double(env, d1);
    if (!enif_get_double(env,term, &d2) || d1 != d2) {
	fprintf(stderr, "test_double(%e) ...FAILED i2=%e\r\n", d1, d2);
	return 0;
    }
    return 1;
}

#define TAG_BITS        4
#define SMALL_BITS	(sizeof(void*)*8 - TAG_BITS)
#ifdef _WIN64
#define MAX_SMALL	((1LL << (SMALL_BITS-1))-1)
#define MIN_SMALL	(-(1LL << (SMALL_BITS-1)))
#else
#define MAX_SMALL	((1L << (SMALL_BITS-1))-1)
#define MIN_SMALL	(-(1L << (SMALL_BITS-1)))
#endif

static ERL_NIF_TERM type_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    int i;
    int sint;
    unsigned uint;
    long slong;
    unsigned long ulong;
    ErlNifSInt64 sint64;
    ErlNifUInt64 uint64;
    double d;
    ERL_NIF_TERM atom, ref1, ref2;

    sint = INT_MIN;
    do {
	if (!test_int(env,sint)) {
	    goto error;
	}
	sint += ~sint / 3 + 1;
    } while (sint < 0);
    sint = INT_MAX;
    do {
	if (!test_int(env,sint)) {
	    goto error;
	}
	sint -= sint / 3 + 1;
    } while (sint >= 0);

    slong = LONG_MIN;
    do {
	if (!test_long(env,slong)) {
	    goto error;
	}
	slong += ~slong / 3 + 1;
    } while (slong < 0);
    slong = LONG_MAX;
    do {
	if (!test_long(env,slong)) {
	    goto error;
	}
	slong -= slong / 3 + 1;
    } while (slong >= 0);

    sint64 = ((ErlNifSInt64)1 << 63); /* INT64_MIN */
    do {
	if (!test_int64(env,sint64)) {
	    goto error;
	}
	sint64 += ~sint64 / 3 + 1;
    } while (sint64 < 0);
    sint64 = ((ErlNifUInt64)1 << 63) - 1; /* INT64_MAX */
    do {
	if (!test_int64(env,sint64)) {
	    goto error;
	}
	sint64 -= sint64 / 3 + 1;
    } while (sint64 >= 0);

    uint = UINT_MAX;
    for (;;) {
	if (!test_uint(env,uint)) {
	    goto error;
	}
	if (uint == 0) break;
	uint -= uint / 3 + 1;
    }
    ulong = ULONG_MAX;
    for (;;) {
	if (!test_ulong(env,ulong)) {
	    goto error;
	}
	if (ulong == 0) break;
	ulong -= ulong / 3 + 1;
    }    
    uint64 = (ErlNifUInt64)-1; /* UINT64_MAX */
    for (;;) {
	if (!test_uint64(env,uint64)) {
	    goto error;
	}
	if (uint64 == 0) break;
	uint64 -= uint64 / 3 + 1;
    }    

    if (MAX_SMALL < INT_MAX) { /* 32-bit */
	for (i=-10 ; i <= 10; i++) {
	    if (!test_int(env,MAX_SMALL+i)) {
		goto error;
	    }
	}
	for (i=-10 ; i <= 10; i++) {
	    if (!test_int(env,MIN_SMALL+i)) {
		goto error;
	    }
	}
	for (i=-10 ; i <= 10; i++) {
	    if (!test_uint(env,MAX_SMALL+i)) {
		goto error;
	    }
	}
    }
    assert((MAX_SMALL < INT_MAX) == (MIN_SMALL > INT_MIN));

    for (i=-10 ; i < 10; i++) {
	if (!test_long(env,MAX_SMALL+i) || !test_ulong(env,MAX_SMALL+i) ||
	    !test_long(env,MIN_SMALL+i) ||
	    !test_int64(env,MAX_SMALL+i) || !test_uint64(env,MAX_SMALL+i) ||
	    !test_int64(env,MIN_SMALL+i)) {
	    goto error;
	}
	if (MAX_SMALL < INT_MAX) {
	    if (!test_int(env,MAX_SMALL+i) || !test_uint(env,MAX_SMALL+i) ||
		!test_int(env,MIN_SMALL+i)) {
		goto error;
	    }
	}
    }
    for (d=3.141592e-100 ; d < 1e100 ; d *= 9.97) {
	if (!test_double(env,d) || !test_double(env,-d)) {
	    goto error;
	}	
    }

    if (!enif_make_existing_atom(env,"nif_SUITE", &atom, ERL_NIF_LATIN1)
	|| !enif_is_identical(atom,enif_make_atom(env,"nif_SUITE"))) {
	fprintf(stderr, "nif_SUITE not an atom?\r\n");
	goto error;
    }
    for (i=2; i; i--) {
	if (enif_make_existing_atom(env,"nif_SUITE_pink_unicorn", &atom, ERL_NIF_LATIN1)) {
	    fprintf(stderr, "pink unicorn exist?\r\n");
	    goto error;
	}
    }

    ref1 = enif_make_ref(env);
    ref2 = enif_make_ref(env);
    if (!enif_is_ref(env,ref1) || !enif_is_ref(env,ref2) 
	|| enif_is_identical(ref1,ref2) || enif_compare(ref1,ref2)==0) {
	fprintf(stderr, "strange refs?\r\n");
	goto error;
    }
    return enif_make_atom(env,"ok");

error:
    return enif_make_atom(env,"error");
}

static ERL_NIF_TERM echo_int(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    int sint;
    unsigned uint;
    long slong;
    unsigned long ulong;
    ErlNifSInt64 sint64;
    ErlNifUInt64 uint64;    
    ERL_NIF_TERM sint_term = atom_false,   uint_term = atom_false;
    ERL_NIF_TERM slong_term = atom_false,  ulong_term = atom_false;
    ERL_NIF_TERM sint64_term = atom_false, uint64_term = atom_false;

    if (enif_get_int(env, argv[0], &sint)) {
	sint_term = enif_make_int(env, sint);
    }
    if (enif_get_uint(env, argv[0], &uint)) {
	uint_term = enif_make_uint(env, uint);
    }
    if (enif_get_long(env, argv[0], &slong)) {
	slong_term = enif_make_long(env, slong);
    }
    if (enif_get_ulong(env, argv[0], &ulong)) {
	ulong_term = enif_make_ulong(env, ulong);
    }
    if (enif_get_int64(env, argv[0], &sint64)) {
	sint64_term = enif_make_int64(env, sint64);
    }
    if (enif_get_uint64(env, argv[0], &uint64)) {
	uint64_term = enif_make_uint64(env, uint64);
    }
    return enif_make_list6(env, sint_term, uint_term, slong_term, ulong_term, sint64_term, uint64_term);
}

static ERL_NIF_TERM type_sizes(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    return enif_make_tuple2(env, enif_make_int(env, sizeof(int)),
			    enif_make_int(env, sizeof(long)));
}

static ERL_NIF_TERM tuple_2_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    int arity = -1;
    const ERL_NIF_TERM* ptr;
    ERL_NIF_TERM list = enif_make_list(env,0);

    if (argc!=1 || !enif_get_tuple(env,argv[0],&arity,&ptr)) {
	return enif_make_badarg(env);
    }
    while (--arity >= 0) {
	list = enif_make_list_cell(env,ptr[arity],list);
    }
    return list;
}

static ERL_NIF_TERM is_identical(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{    
    if (argc != 2) {
	return enif_make_badarg(env);
    }
    return enif_make_atom(env, (enif_is_identical(argv[0],argv[1]) ?
				"true" : "false"));
}

static ERL_NIF_TERM compare(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{    
    if (argc != 2) {
	return enif_make_badarg(env);
    }
    return enif_make_int(env, enif_compare(argv[0],argv[1]));
}

static ERL_NIF_TERM many_args_100(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    int i, k;
    if (argc == 100) {
	for (i=1; i<=100; i++) {
	    if (!enif_get_int(env,argv[i-1],&k) || k!=i) {
		goto badarg;
	    }
	}
	return enif_make_atom(env,"ok");
    }
badarg:
    return enif_make_badarg(env);
}

static ERL_NIF_TERM clone_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary ibin;
    if (enif_inspect_binary(env,argv[0],&ibin)) {
	ERL_NIF_TERM obin;	
	memcpy(enif_make_new_binary(env, ibin.size, &obin),
	       ibin.data, ibin.size);
	return obin;
    }
    else {
	return enif_make_badarg(env);
    }
}

static ERL_NIF_TERM make_sub_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    int pos, size;
    if (!enif_get_int(env,argv[1],&pos) || !enif_get_int(env,argv[2],&size)) {
	return enif_make_badarg(env);
    }
    return enif_make_sub_binary(env,argv[0],pos,size);
}

static ERL_NIF_TERM string_to_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary obin;
    unsigned size;
    int n;
    if (!enif_get_int(env,argv[1],(int*)&size) 
	|| !enif_alloc_binary(size,&obin)) {
	return enif_make_badarg(env);
    }
    n = enif_get_string(env, argv[0], (char*)obin.data, size, ERL_NIF_LATIN1);
    return enif_make_tuple(env, 2, enif_make_int(env,n),
			   enif_make_binary(env,&obin));
}

static ERL_NIF_TERM atom_to_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary obin;
    unsigned size;
    int n;
    if (!enif_get_int(env,argv[1],(int*)&size) 
	|| !enif_alloc_binary(size,&obin)) {
	return enif_make_badarg(env);
    }
    n = enif_get_atom(env, argv[0], (char*)obin.data, size, ERL_NIF_LATIN1);
    return enif_make_tuple(env, 2, enif_make_int(env,n),
			   enif_make_binary(env,&obin));
}

static ERL_NIF_TERM macros(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    const ERL_NIF_TERM* a;
    ERL_NIF_TERM lists, tuples;
    int arity;
    if (!enif_get_tuple(env, argv[0], &arity, &a) || arity != 9) {
	return enif_make_badarg(env);
    }
    
    lists = enif_make_list(env,9,
			   enif_make_list1(env,a[0]),
			   enif_make_list2(env,a[0],a[1]),
			   enif_make_list3(env,a[0],a[1],a[2]),
			   enif_make_list4(env,a[0],a[1],a[2],a[3]),
			   enif_make_list5(env,a[0],a[1],a[2],a[3],a[4]),
			   enif_make_list6(env,a[0],a[1],a[2],a[3],a[4],a[5]),
			   enif_make_list7(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6]),
			   enif_make_list8(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]),
			   enif_make_list9(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]));
    tuples = enif_make_list(env,9,
			    enif_make_tuple1(env,a[0]),
			    enif_make_tuple2(env,a[0],a[1]),
			    enif_make_tuple3(env,a[0],a[1],a[2]),
			    enif_make_tuple4(env,a[0],a[1],a[2],a[3]),
			    enif_make_tuple5(env,a[0],a[1],a[2],a[3],a[4]),
			    enif_make_tuple6(env,a[0],a[1],a[2],a[3],a[4],a[5]),
			    enif_make_tuple7(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6]),
			    enif_make_tuple8(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]),
			    enif_make_tuple9(env,a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7],a[8]));
    return enif_make_tuple2(env,lists,tuples);
}

static ERL_NIF_TERM tuple_2_list_and_tuple(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    const ERL_NIF_TERM* arr;
    int arity;
    if (!enif_get_tuple(env,argv[0],&arity,&arr)) {
	return enif_make_badarg(env);
    }
    return enif_make_tuple2(env, 
			    enif_make_list_from_array(env, arr, arity),
			    enif_make_tuple_from_array(env, arr, arity));
}

static ERL_NIF_TERM iolist_2_bin(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary obin;
    if (!enif_inspect_iolist_as_binary(env, argv[0], &obin)) {
	return enif_make_badarg(env);
    }
    return enif_make_binary(env,&obin);
}

static ERL_NIF_TERM last_resource_dtor_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM ret;
    if (resource_dtor_last != NULL) {
	ERL_NIF_TERM bin;	
	memcpy(enif_make_new_binary(env, resource_dtor_last_sz, &bin),
	       resource_dtor_last_data, resource_dtor_last_sz);
	ret = enif_make_tuple3(env,
				make_pointer(env, resource_dtor_last),
				bin,  
				enif_make_int(env, resource_dtor_cnt));
    }
    else {
	ret = enif_make_list(env,0);
    }
    resource_dtor_last = NULL;
    resource_dtor_last_sz = 0;
    resource_dtor_cnt = 0;
    return ret;
}

static ERL_NIF_TERM get_resource_type(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    PrivData* data = (PrivData*) enif_priv_data(env);
    int ix;
    
    if (!enif_get_int(env, argv[0], &ix) || ix >= 2) {
	return enif_make_badarg(env);
    }
    return make_pointer(env, data->rt_arr[ix].vp);
}

static ERL_NIF_TERM alloc_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary data_bin;
    union { ErlNifResourceType* t; void* vp; } type;
    void* data;
    if (!get_pointer(env, argv[0], &type.vp)
	|| !enif_inspect_binary(env, argv[1], &data_bin)
	|| (data = enif_alloc_resource(type.t, data_bin.size))==NULL) {

	return enif_make_badarg(env);
    }
    memcpy(data, data_bin.data, data_bin.size);
    return make_pointer(env, data);
}

static ERL_NIF_TERM make_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    void* data;
    if (!get_pointer(env, argv[0], &data)) {
	return enif_make_badarg(env);
    }
    return enif_make_resource(env, data);
}

static ERL_NIF_TERM make_new_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary data_bin;
    union { ErlNifResourceType* t; void* vp; } type;
    void* data;
    ERL_NIF_TERM ret;
    if (!get_pointer(env, argv[0], &type.vp)
	|| !enif_inspect_binary(env, argv[1], &data_bin)
	|| (data = enif_alloc_resource(type.t, data_bin.size))==NULL) {

	return enif_make_badarg(env);
    }
    ret = enif_make_resource(env, data);
    memcpy(data, data_bin.data, data_bin.size);
    enif_release_resource(data);
    return ret;
}

static ERL_NIF_TERM make_new_resource_binary(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary data_bin;
    union { struct binary_resource* p; void* vp; } br;
    void* buf;
    ERL_NIF_TERM ret;
    if (!enif_inspect_binary(env, argv[0], &data_bin)
	|| (br.vp = enif_alloc_resource(binary_resource_type,
					sizeof(struct binary_resource)))==NULL
	|| (buf = enif_alloc(data_bin.size)) == NULL) {
	
	return enif_make_badarg(env);
    }    
    memset(br.vp,0xba,sizeof(struct binary_resource)); /* avoid valgrind warning */
    br.p->data = buf;
    br.p->size = data_bin.size;
    memcpy(br.p->data, data_bin.data, data_bin.size);    
    ret = enif_make_resource_binary(env, br.vp, br.p->data, br.p->size);    
    enif_release_resource(br.p);
    return enif_make_tuple2(env, make_pointer(env,br.vp), ret);
}

static void binary_resource_dtor(ErlNifEnv* env, void* obj)
{
    struct binary_resource* br = (struct binary_resource*) obj;
    resource_dtor(env,obj);
    assert(br->data != NULL);
    enif_free(br->data);
    br->data = NULL;
}

static ERL_NIF_TERM get_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary data_bin;
    union { ErlNifResourceType* t; void* vp; } type;
    void* data;

    type.t = NULL;
    if (enif_is_identical(argv[0], atom_binary_resource_type)) {
	type.t = binary_resource_type;
    }
    else if (enif_is_identical(argv[0], atom_monitor_resource_type)) {
	type.t = monitor_resource_type;
    }
    else {
	get_pointer(env, argv[0], &type.vp);
    }
    if (type.t == NULL 
	|| !enif_get_resource(env, argv[1], type.t, &data)) {
	return enif_make_badarg(env);
    }
    enif_alloc_binary(enif_sizeof_resource(data), &data_bin);    
    memcpy(data_bin.data, data, data_bin.size);
    return enif_make_tuple2(env, make_pointer(env,data),
			    enif_make_binary(env, &data_bin));
}

static ERL_NIF_TERM release_resource(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    void* data;
    if (!get_pointer(env, argv[0], &data)) {
	return enif_make_badarg(env);
    }
    enif_release_resource(data);
    return enif_make_atom(env,"ok");
}

/*
 * argv[0] an atom
 * argv[1] a binary
 * argv[2] a ref
 * argv[3] 'ok'
 * argv[4] a fun
 * argv[5] a pid
 * argv[6] a port
 * argv[7] an empty list
 * argv[8] a non-empty list
 * argv[9] a tuple
 * argv[10] a number (small, big integer or float)
 */
static ERL_NIF_TERM check_is(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM ok_atom = enif_make_atom(env, "ok");

    if (!enif_is_atom(env, argv[0])) return enif_make_badarg(env);
    if (!enif_is_binary(env, argv[1])) return enif_make_badarg(env);
    if (!enif_is_ref(env, argv[2])) return enif_make_badarg(env);
    if (!enif_is_identical(argv[3], ok_atom)) return enif_make_badarg(env);
    if (!enif_is_fun(env, argv[4])) return enif_make_badarg(env);
    if (!enif_is_pid(env, argv[5])) return enif_make_badarg(env);
    if (!enif_is_port(env, argv[6])) return enif_make_badarg(env);
    if (!enif_is_empty_list(env, argv[7])) return enif_make_badarg(env);
    if (!enif_is_list(env, argv[7])) return enif_make_badarg(env);
    if (!enif_is_list(env, argv[8])) return enif_make_badarg(env);
    if (!enif_is_tuple(env, argv[9])) return enif_make_badarg(env);
    if (!enif_is_number(env, argv[10])) return enif_make_badarg(env);

    return ok_atom;
}

/*
 * no arguments
 *
 * This function is separate from check_is because it calls enif_make_badarg
 * and so it must return the badarg exception as its return value. Thus, the
 * badarg exception indicates success.
 */
static ERL_NIF_TERM check_is_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM badarg, exc_term;
    ERL_NIF_TERM error_atom = enif_make_atom(env, "error");
    ERL_NIF_TERM badarg_atom = enif_make_atom(env, "badarg");
    assert(!enif_is_exception(env, error_atom));
    badarg = enif_make_badarg(env);
    assert(enif_is_exception(env, badarg));
    assert(enif_has_pending_exception(env, NULL));
    assert(enif_has_pending_exception(env, &exc_term));
    assert(enif_is_identical(exc_term, badarg_atom));
    return badarg;
}

/*
 * argv[0] atom with length of 6
 * argv[1] list with length of 6
 * argv[2] empty list
 * argv[3] not an atom
 * argv[4] not a list
 * argv[5] improper list
 */
static ERL_NIF_TERM length_test(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    unsigned len;

    if (!enif_get_atom_length(env, argv[0], &len, ERL_NIF_LATIN1) || len != 6)
	return enif_make_badarg(env);

    if (!enif_get_list_length(env, argv[1], &len) || len != 6)
	return enif_make_badarg(env);

    if (!enif_get_list_length(env, argv[2], &len) || len != 0)
	return enif_make_badarg(env);

    if (enif_get_atom_length(env, argv[3], &len, ERL_NIF_LATIN1))
	return enif_make_badarg(env);

    if (enif_get_list_length(env, argv[4], &len))
	return enif_make_badarg(env);

    if (enif_get_list_length(env, argv[5], &len))
	return enif_make_badarg(env);

    return enif_make_atom(env, "ok");
}

static ERL_NIF_TERM make_atoms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM arr[7];
    ERL_NIF_TERM existingatom0a, existingatom0b;
    ERL_NIF_TERM existing0atom0;
    const char * const an0atom = "an0atom";
    const char an0atom0[8] = {'a','n','\0','a','t','o','m',0};

    arr[0] = enif_make_atom(env, "an0atom");
    arr[1] = enif_make_atom_len(env, "an0atom", 7);
    arr[2] = enif_make_atom_len(env, an0atom, 7);
    arr[3] = enif_make_atom_len(env, an0atom0, 8);

    if (!enif_make_existing_atom(env, "an0atom", &existingatom0a, ERL_NIF_LATIN1))
	return enif_make_atom(env, "error");
    arr[4] = existingatom0a;

    if (!enif_make_existing_atom_len(env, an0atom, 7, &existingatom0b, ERL_NIF_LATIN1))
	return enif_make_atom(env, "error");
    arr[5] = existingatom0b;

    if (!enif_make_existing_atom_len(env, an0atom0, 8, &existing0atom0, ERL_NIF_LATIN1))
	return enif_make_atom(env, "error");
    arr[6] = existing0atom0;

    return enif_make_tuple7(env,
			    arr[0],arr[1],arr[2],arr[3],arr[4],arr[5],arr[6]);
}

static ERL_NIF_TERM make_strings(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    const char a0string[8] = {'a','0','s','t','r','i','n','g'};
    const char a0string0[9] = {'a','\0','s','t','r','i','n','g',0};
    const char astringwith8bits[37] = {'E','r','l','a','n','g',' ',0xE4 /* 'ä' */,'r',' ','e','t','t',' ','g','e','n','e','r','e','l','l','t',' ','p','r','o','g','r','a','m','s','p','r', 0xE5 /* 'å' */,'k',0};

    return enif_make_tuple5(env,
			    enif_make_string(env, "a0string", ERL_NIF_LATIN1),
			    enif_make_string_len(env, "a0string", 8, ERL_NIF_LATIN1),
			    enif_make_string_len(env, a0string, 8, ERL_NIF_LATIN1),
			    enif_make_string_len(env, a0string0, 9, ERL_NIF_LATIN1),
			    enif_make_string(env, astringwith8bits, ERL_NIF_LATIN1));
}
static ERL_NIF_TERM send_list_seq(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifPid to;
    ERL_NIF_TERM msg;
    ErlNifEnv* msg_env;
    int i, res;
    
    if (!enif_get_int(env, argv[0], &i)) {
	return enif_make_badarg(env);
    }
    if (argv[1] == atom_self) {
	enif_self(env, &to);
    }
    else if (!enif_get_local_pid(env, argv[1], &to)) {
	return enif_make_badarg(env);
    }
    msg_env = enif_alloc_env();
    msg = enif_make_list(msg_env,0);
    for ( ; i>0 ; i--) {
	msg = enif_make_list_cell(msg_env, enif_make_int(msg_env, i), msg);
    }
    res = enif_send(env, &to, msg_env, msg);
    enif_free_env(msg_env);
    return enif_make_tuple2(env, atom_ok, enif_make_int(env,res));
}

static void fill(void* dst, unsigned bytes, int seed)
{
    unsigned char* ptr = dst;
    int i;
    for (i=bytes; i>0; i--) {
	*ptr++ = seed;
	seed += 7;
    }
}

#define MAKE_TERM_REUSE_LEN 16
struct make_term_info
{
    ErlNifEnv* caller_env;
    ErlNifEnv* dst_env;
    int dst_env_valid;
    ERL_NIF_TERM reuse[MAKE_TERM_REUSE_LEN];
    unsigned reuse_push;
    unsigned reuse_pull;
    ErlNifResourceType* resource_type;
    ERL_NIF_TERM other_term;
    ERL_NIF_TERM blob;
    ErlNifPid to_pid;
    ErlNifTid tid;
    ErlNifCond* cond;
    ErlNifMutex* mtx;
    int send_it;
    int send_res;
    unsigned n;
};


static void push_term(struct make_term_info* mti, ERL_NIF_TERM term)
{
    unsigned ix = (mti->reuse_push++) % MAKE_TERM_REUSE_LEN; 
    mti->reuse[ix] = term;
    //enif_fprintf(stderr, "push at %u: %T\r\n", ix, term);
}
static ERL_NIF_TERM pull_term(struct make_term_info* mti)
{
    unsigned ix;
    if (mti->reuse_pull >= mti->reuse_push &&
	mti->reuse_push < MAKE_TERM_REUSE_LEN) {
	mti->reuse_pull = 0;
	if (mti->reuse_push == 0) {
            assert(mti->dst_env_valid);
	    mti->reuse[0] = enif_make_list(mti->dst_env, 0);
	}
    }
    ix = (mti->reuse_pull++) % MAKE_TERM_REUSE_LEN;
    //enif_fprintf(stderr, "pull from %u: %T\r\n", ix, mti->reuse[ix]);
    return mti->reuse[ix];
}

static int make_term_n(struct make_term_info* mti, int n, ERL_NIF_TERM* res);

static ERL_NIF_TERM make_term_binary(struct make_term_info* mti, int n)
{	
    ErlNifBinary bin;
    enif_alloc_binary(100, &bin);
    fill(bin.data, bin.size, n);
    return enif_make_binary(mti->dst_env, &bin);
}

static ERL_NIF_TERM make_term_int(struct make_term_info* mti, int n)
{
    int i;
    fill(&i, sizeof(i), n);
    return enif_make_int(mti->dst_env, i);
}

static ERL_NIF_TERM make_term_ulong(struct make_term_info* mti, int n)
{
    unsigned long ul;
    fill(&ul, sizeof(ul), n);
    return enif_make_ulong(mti->dst_env, ul);
}

static ERL_NIF_TERM make_term_double(struct make_term_info* mti, int n)
{
    double d = 3.141592;
    return enif_make_double(mti->dst_env, d);    	
}
static ERL_NIF_TERM make_term_atom(struct make_term_info* mti, int n)
{
    return enif_make_atom(mti->dst_env, "make_term_n");    
} 
static ERL_NIF_TERM make_term_existing_atom(struct make_term_info* mti, int n)
{	
    ERL_NIF_TERM res;
    int exist = enif_make_existing_atom(mti->dst_env, "nif_SUITE", &res,
					ERL_NIF_LATIN1);
    assert(exist);
    return res;
}
static ERL_NIF_TERM make_term_string(struct make_term_info* mti, int n)
{
    return enif_make_string(mti->dst_env, "Hello!", ERL_NIF_LATIN1);
}
static ERL_NIF_TERM make_term_sub_binary(struct make_term_info* mti, int n)
{
    ERL_NIF_TERM orig;
    unsigned char* ptr = enif_make_new_binary(mti->dst_env, 10, &orig);
    fill(ptr, 10, n);
    return enif_make_sub_binary(mti->dst_env, orig, 3, 5);	
}
static ERL_NIF_TERM make_term_uint(struct make_term_info* mti, int n)
{
    unsigned int ui;
    fill(&ui, sizeof(ui), n);
    return enif_make_uint(mti->dst_env, ui);	       
}
static ERL_NIF_TERM make_term_long(struct make_term_info* mti, int n)
{
    long l;
    fill(&l, sizeof(l), n);
    return enif_make_long(mti->dst_env, l);
}
static ERL_NIF_TERM make_term_tuple0(struct make_term_info* mti, int n)
{
    return enif_make_tuple(mti->dst_env, 0);
}
static ERL_NIF_TERM make_term_list0(struct make_term_info* mti, int n)
{
    return enif_make_list(mti->dst_env, 0);
}
static ERL_NIF_TERM make_term_resource(struct make_term_info* mti, int n)
{
    void* resource = enif_alloc_resource(mti->resource_type, 10);
    ERL_NIF_TERM term;
    fill(resource, 10, n); 
    term = enif_make_resource(mti->dst_env, resource);
    enif_release_resource(resource);
    return term;
}
static ERL_NIF_TERM make_term_new_binary(struct make_term_info* mti, int n)
{
    ERL_NIF_TERM res;
    unsigned char* ptr = enif_make_new_binary(mti->dst_env,20,&res);
    fill(ptr, 20, n);	
    return res;
}
static ERL_NIF_TERM make_term_caller_pid(struct make_term_info* mti, int n)
{
    ErlNifPid pid;    
    return enif_make_pid(mti->dst_env, enif_self(mti->caller_env, &pid));    		
}	

static ERL_NIF_TERM make_term_tuple(struct make_term_info* mti, int n)
{
    ERL_NIF_TERM t[3];
    t[0] = pull_term(mti);
    t[1] = pull_term(mti);
    t[2] = pull_term(mti);
    return enif_make_tuple3(mti->dst_env, t[0], t[1], t[2]);	      
}
static ERL_NIF_TERM make_term_list(struct make_term_info* mti, int n)
{
    ERL_NIF_TERM t[3];
    t[0] = pull_term(mti);
    t[1] = pull_term(mti);
    t[2] = pull_term(mti);
    return enif_make_list3(mti->dst_env, t[0], t[1], t[2]);    
}
static ERL_NIF_TERM make_term_list_cell(struct make_term_info* mti, int n)
{
    ERL_NIF_TERM t[2];
    t[0] = pull_term(mti);
    t[1] = pull_term(mti);
    return enif_make_list_cell(mti->dst_env, t[0], t[1]);
}
static ERL_NIF_TERM make_term_tuple_from_array(struct make_term_info* mti, int n)
{
    ERL_NIF_TERM t[3];
    t[0] = pull_term(mti);
    t[1] = pull_term(mti);
    t[2] = pull_term(mti);
    return enif_make_tuple_from_array(mti->dst_env, t, 3);	
}
static ERL_NIF_TERM make_term_list_from_array(struct make_term_info* mti, int n)
{
    ERL_NIF_TERM t[3];
    t[0] = pull_term(mti);
    t[1] = pull_term(mti);
    t[2] = pull_term(mti);
    return enif_make_list_from_array(mti->dst_env, t, 3);    
}
static ERL_NIF_TERM make_term_garbage(struct make_term_info* mti, int n)
{
    (void) enif_make_string(mti->dst_env, "garbage string", ERL_NIF_LATIN1);
    return pull_term(mti);
}
static ERL_NIF_TERM make_term_copy(struct make_term_info* mti, int n)
{
    return enif_make_copy(mti->dst_env, mti->other_term);
}

typedef ERL_NIF_TERM Make_term_Func(struct make_term_info*, int);
static Make_term_Func* make_funcs[] = {
    make_term_binary,
    make_term_int,
    make_term_ulong,
    make_term_double,
    make_term_atom,
    make_term_existing_atom,
    make_term_string,
    make_term_sub_binary,
    make_term_uint,
    make_term_long,
    make_term_tuple0,
    make_term_list0,
    make_term_resource,
    make_term_new_binary,
    make_term_caller_pid,
    make_term_tuple,
    make_term_list,
    make_term_list_cell,
    make_term_tuple_from_array,
    make_term_list_from_array,
    make_term_garbage,
    make_term_copy
};
static unsigned num_of_make_funcs()
{
    return sizeof(make_funcs)/sizeof(*make_funcs);
}
static int make_term_n(struct make_term_info* mti, int n, ERL_NIF_TERM* res)
{
    if (n < num_of_make_funcs()) {
        assert(mti->dst_env_valid);
	*res = make_funcs[n](mti, n);
	push_term(mti, *res);
	return 1;
    }
    return 0;
}

static ERL_NIF_TERM make_blob(ErlNifEnv* caller_env, ErlNifEnv* dst_env,
			      ERL_NIF_TERM other_term)
{
    PrivData* priv = (PrivData*) enif_priv_data(caller_env);
    ERL_NIF_TERM term, list;
    int n = 0;
    struct make_term_info mti;
    mti.caller_env = caller_env;
    mti.dst_env = dst_env;
    mti.dst_env_valid = 1;
    mti.reuse_push = 0;
    mti.reuse_pull = 0;
    mti.resource_type = priv->rt_arr[0].t;
    mti.other_term = other_term;

    list = enif_make_list(dst_env, 0); 
    while (make_term_n(&mti, n++, &term)) {
	list = enif_make_list_cell(dst_env, term, list);
    }
    return list;
}

static ERL_NIF_TERM send_new_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifPid to;
    ERL_NIF_TERM msg, copy;
    ErlNifEnv* msg_env;
    int res;
    
    if (!enif_get_local_pid(env, argv[0], &to)) {
	return enif_make_badarg(env);
    }
    msg_env = enif_alloc_env();
    msg = make_blob(env,msg_env, argv[1]);
    copy = make_blob(env,env, argv[1]);
    res = enif_send(env, &to, msg_env, msg);
    enif_free_env(msg_env);
    return enif_make_tuple3(env, atom_ok, enif_make_int(env,res), copy);
}

static ERL_NIF_TERM alloc_msgenv(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    PrivData* priv = (PrivData*) enif_priv_data(env);
    struct make_term_info* mti;
    ERL_NIF_TERM ret;

    mti = (struct make_term_info*) enif_alloc_resource(msgenv_resource_type,
						       sizeof(*mti));
    mti->caller_env = NULL;
    mti->dst_env = enif_alloc_env();
    mti->dst_env_valid = 1;
    mti->reuse_push = 0;
    mti->reuse_pull = 0;
    mti->resource_type = priv->rt_arr[0].t;
    mti->other_term = enif_make_list(mti->dst_env, 0);
    mti->blob = enif_make_list(mti->dst_env, 0);
    mti->mtx = enif_mutex_create("nif_SUITE:mtx");
    mti->cond = enif_cond_create("nif_SUITE:cond");
    mti->send_res = 0xcafebabe;
    mti->n = 0;
    ret = enif_make_resource(env, mti);
    enif_release_resource(mti);
    return ret;
}

static void msgenv_dtor(ErlNifEnv* env, void* obj)
{
    struct make_term_info* mti = (struct make_term_info*) obj;
    if (mti->dst_env != NULL) {
	enif_free_env(mti->dst_env);
    }
    enif_mutex_destroy(mti->mtx);
    enif_cond_destroy(mti->cond);
}

static ERL_NIF_TERM clear_msgenv(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    mti_t mti;
    if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)) {
	return enif_make_badarg(env);
    }
    enif_clear_env(mti.p->dst_env);
    mti.p->dst_env_valid = 1;
    mti.p->reuse_pull = 0;
    mti.p->reuse_push = 0;
    mti.p->blob = enif_make_list(mti.p->dst_env, 0);
    return atom_ok;
}

static ERL_NIF_TERM grow_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    mti_t mti;
    ERL_NIF_TERM term;
    if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)
	|| (argc>2 && !enif_get_uint(env,argv[2], &mti.p->n))) {
	return enif_make_badarg(env);
    }
    mti.p->caller_env = env;
    mti.p->other_term = argv[1];
    mti.p->n %= num_of_make_funcs();
    make_term_n(mti.p, mti.p->n++, &term);
    mti.p->blob = enif_make_list_cell(mti.p->dst_env, term, mti.p->blob);
    return atom_ok;
}

static ERL_NIF_TERM send_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    mti_t mti;
    ErlNifPid to;
    ERL_NIF_TERM copy;
    int res;
    if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)
	|| !enif_get_local_pid(env, argv[1], &to)) {
	return enif_make_badarg(env);
    }
    copy = enif_make_copy(env, mti.p->blob);
    res = enif_send(env, &to, mti.p->dst_env, mti.p->blob);
    if (res)
        mti.p->dst_env_valid = 0;
    return enif_make_tuple3(env, atom_ok, enif_make_int(env,res), copy);
}

static ERL_NIF_TERM send3_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    mti_t mti;
    ErlNifPid to;
    int res;
    if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)
	|| !enif_get_local_pid(env, argv[1], &to)) {
	return enif_make_badarg(env);
    }
    mti.p->blob = enif_make_tuple2(mti.p->dst_env, 
				   enif_make_copy(mti.p->dst_env, argv[2]),
				   mti.p->blob);
    res = enif_send(env, &to, mti.p->dst_env, mti.p->blob);
    if (res)
        mti.p->dst_env_valid = 0;
    return enif_make_int(env,res);
}

void* threaded_sender(void *arg)
{

    mti_t mti;
    mti.vp = arg;

    enif_mutex_lock(mti.p->mtx);
    while (!mti.p->send_it) {
	enif_cond_wait(mti.p->cond, mti.p->mtx);
    }
    mti.p->send_it = 0;
    enif_mutex_unlock(mti.p->mtx);
    mti.p->send_res = enif_send(NULL, &mti.p->to_pid, mti.p->dst_env, mti.p->blob);
    if (mti.p->send_res)
        mti.p->dst_env_valid = 0;
    return NULL;
}

static ERL_NIF_TERM send_blob_thread(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    mti_t mti;
    ERL_NIF_TERM copy;
    if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)
	|| !enif_get_local_pid(env,argv[1], &mti.p->to_pid)) {
	return enif_make_badarg(env);
    }
    copy = enif_make_copy(env, mti.p->blob);

    mti.p->send_it = enif_is_identical(argv[2],atom_join);
    if (enif_thread_create("nif_SUITE:send_from_thread", &mti.p->tid,
			   threaded_sender, mti.p, NULL) != 0) {
	return enif_make_badarg(env);
    }
    if (enif_is_identical(argv[2],atom_join)) {
	int err = enif_thread_join(mti.p->tid, NULL);
	assert(err == 0);
	return enif_make_tuple3(env, atom_ok, enif_make_int(env, mti.p->send_res), copy);
    }
    else {
	enif_keep_resource(mti.vp);
	return enif_make_tuple2(env, atom_ok, copy);
    }
}

static ERL_NIF_TERM join_send_thread(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    mti_t mti;
    int err;
    if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)) {
	return enif_make_badarg(env);
    }
    enif_mutex_lock(mti.p->mtx);
    mti.p->send_it = 1;
    enif_cond_signal(mti.p->cond);
    enif_mutex_unlock(mti.p->mtx);
    err = enif_thread_join(mti.p->tid, NULL);
    assert(err == 0);
    enif_release_resource(mti.vp);
    return enif_make_tuple2(env, atom_ok, enif_make_int(env, mti.p->send_res));    
}

static ERL_NIF_TERM copy_blob(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    mti_t mti;
    if (!enif_get_resource(env, argv[0], msgenv_resource_type, &mti.vp)) {
	return enif_make_badarg(env);
    }
    return enif_make_copy(env, mti.p->blob);
}

static ERL_NIF_TERM send_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifEnv* menv;
    ErlNifPid pid;
    int ret;
    if (!enif_get_local_pid(env, argv[0], &pid)) {
	return enif_make_badarg(env);
    }
    menv = enif_alloc_env();
    ret = enif_send(env, &pid, menv, enif_make_copy(menv, argv[1]));
    enif_free_env(menv);
    return enif_make_int(env, ret);
}

static ERL_NIF_TERM send_copy_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifPid pid;
    int ret;
    if (!enif_get_local_pid(env, argv[0], &pid)) {
	return enif_make_badarg(env);
    }
    ret = enif_send(env, &pid, NULL, argv[1]);
    return enif_make_int(env, ret);
}

static ERL_NIF_TERM reverse_list(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {
    ERL_NIF_TERM rev_list;

    if(!enif_make_reverse_list(env, argv[0], &rev_list))
	return enif_make_atom(env, "badarg");
    return rev_list;
}

static ERL_NIF_TERM otp_9668_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    /* Inspect in process independent env */
    ErlNifEnv* myenv = enif_alloc_env();
    ERL_NIF_TERM mycopy = enif_make_copy(myenv, argv[0]);
    ErlNifBinary obin, cbin;

    if ((enif_inspect_binary(env, argv[0], &obin)
	 && enif_inspect_binary(myenv, mycopy, &cbin))
	||
	(enif_inspect_iolist_as_binary(env, argv[0], &obin)
	 && enif_inspect_iolist_as_binary(myenv, mycopy, &cbin)))
    {
	assert(obin.size == cbin.size);
	assert(memcmp(obin.data, cbin.data, obin.size) == 0);
    }	
    enif_free_env(myenv);
    return atom_ok;
}

static ERL_NIF_TERM otp_9828_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    /* copy a writable binary could reallocate it due to "emasculation"
       and thereby render a previous inspection invalid.
     */
    ErlNifBinary bin1;
    ErlNifEnv* myenv;

    if (!enif_inspect_binary(env, argv[0], &bin1)) {
	return enif_make_badarg(env);
    }

    myenv = enif_alloc_env();
    enif_make_copy(myenv, argv[0]);
    enif_free_env(myenv);

    return memcmp(bin1.data, "I'm alive!", 10)==0 ? atom_ok : atom_false;
}


static ERL_NIF_TERM consume_timeslice_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    int percent;
    char atom[10];

    if (!enif_get_int(env, argv[0], &percent) ||
	!enif_get_atom(env, argv[1], atom, sizeof(atom), ERL_NIF_LATIN1)) {
	return enif_make_badarg(env);
    }
    if (strcmp(atom , "true") == 0) {
	int cnt = 1;
	while (enif_consume_timeslice(env, percent) == 0 && cnt < 200)
	    cnt++;
	return enif_make_int(env, cnt);
    }
    else {
	return enif_make_int(env, enif_consume_timeslice(env, percent));
    }
}

static ERL_NIF_TERM nif_sched2(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    char s[64];
    if (!enif_get_string(env, argv[2], s, sizeof s, ERL_NIF_LATIN1))
	return enif_make_badarg(env);
    return enif_make_tuple2(env, argv[3], argv[2]);
}

static ERL_NIF_TERM nif_sched1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM new_argv[4];
    new_argv[0] = enif_make_atom(env, "garbage0");
    new_argv[1] = enif_make_atom(env, "garbage1");
    new_argv[2] = argv[0];
    new_argv[3] = argv[1];
    return enif_schedule_nif(env, "nif_sched2", 0, nif_sched2, 4, new_argv);
}

static ERL_NIF_TERM call_nif_schedule(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM result;
    if (argc != 2)
	return enif_make_atom(env, "false");
    result = enif_schedule_nif(env, "nif_sched1", 0, nif_sched1, argc, argv);
    assert(!enif_is_exception(env, result));
    return result;
}

/*
 * If argv[0] is the integer 0, call enif_make_badarg, but don't return its
 * return value. Instead, return ok.  Result should still be a badarg
 * exception for the erlang caller.
 *
 * For any other value of argv[0], use it as an exception term and return
 * the exception.
 */
static ERL_NIF_TERM call_nif_exception(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM exc_term;
    ERL_NIF_TERM badarg_atom = enif_make_atom(env, "badarg");
    int arg;

    if (enif_get_int(env, argv[0], &arg) && arg == 0) {
	/* ignore return value */ enif_make_badarg(env);
	assert(enif_has_pending_exception(env, NULL));
	assert(enif_has_pending_exception(env, &exc_term));
	assert(enif_is_identical(badarg_atom, exc_term));
	return enif_make_atom(env, "ok");
    } else {
	ERL_NIF_TERM exc_retval = enif_raise_exception(env, argv[0]);
	assert(enif_has_pending_exception(env, NULL));
	assert(enif_has_pending_exception(env, &exc_term));
	assert(enif_is_identical(argv[0], exc_term));
	return exc_retval;
    }
}

#if !defined(NAN) || !defined(INFINITY)
double zero(void)
{
    return 0.0;
}
#endif

static ERL_NIF_TERM call_nif_nan_or_inf(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    double val;
    char arg[6];
    ERL_NIF_TERM res;

    assert(argc == 1);
    enif_get_atom(env, argv[0], arg, sizeof arg, ERL_NIF_LATIN1);
    if (strcmp(arg, "nan") == 0) {
        /* Verify that enif_make_double raises a badarg for NaN */
#ifdef NAN
        val = NAN;
#else
        val = 0.0/zero();
#endif
    } else {
        /* Verify that enif_make_double raises a badarg for NaN and infinity */
#ifdef INFINITY
        val = INFINITY;
#else
        val = 1.0/zero();
#endif
    }
    res = enif_make_double(env, val);
    assert(enif_is_exception(env, res));
    assert(enif_has_pending_exception(env, NULL));
    if (strcmp(arg, "tuple") == 0) {
        return enif_make_tuple2(env, argv[0], res);
    } else {
        return res;
    }
}

static ERL_NIF_TERM call_nif_atom_too_long(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    char str[257];
    char arg[4];
    size_t len;
    int i;
    ERL_NIF_TERM res;

    assert(argc == 1);
    enif_get_atom(env, argv[0], arg, sizeof arg, ERL_NIF_LATIN1);
    /* Verify that creating an atom from a string that's too long results in a badarg */
    for (i = 0; i < sizeof str; ++i) {
        str[i] = 'a';
    }
    str[256] = '\0';
    if (strcmp(arg, "len") == 0) {
        len = strlen(str);
        res = enif_make_atom_len(env, str, len);
    } else {
        res = enif_make_atom(env, str);
    }
    assert(enif_is_exception(env, res));
    return res;
}

static ERL_NIF_TERM is_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    return enif_make_int(env, enif_is_map(env,argv[0]));
}
static ERL_NIF_TERM get_map_size_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    size_t size = (size_t)-123;
    int ret = enif_get_map_size(env, argv[0], &size);
    return enif_make_tuple2(env, enif_make_int(env, ret), enif_make_int(env, (int)size));
}
static ERL_NIF_TERM make_new_map_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    return enif_make_new_map(env);
}
static ERL_NIF_TERM make_map_put_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
    int ret = enif_make_map_put(env, argv[0], argv[1], argv[2], &map_out);
    return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
}
static ERL_NIF_TERM get_map_value_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM value = enif_make_atom(env, "undefined");
    int ret = enif_get_map_value(env, argv[0], argv[1], &value);
    return enif_make_tuple2(env, enif_make_int(env,ret), value);

}
static ERL_NIF_TERM make_map_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
    int ret = enif_make_map_update(env, argv[0], argv[1], argv[2], &map_out);
    return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
}
static ERL_NIF_TERM make_map_remove_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM map_out = enif_make_atom(env, "undefined");
    int ret = enif_make_map_remove(env, argv[0], argv[1], &map_out);
    return enif_make_tuple2(env, enif_make_int(env,ret), map_out);
}

/* maps */
static ERL_NIF_TERM maps_from_list_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM cell = argv[0];
    ERL_NIF_TERM map  = enif_make_new_map(env);
    ERL_NIF_TERM tuple;
    const ERL_NIF_TERM *pair;
    int arity = -1;

    if (argc != 1 && !enif_is_list(env, cell)) return enif_make_badarg(env);

    /* assume sorted keys */

    while (!enif_is_empty_list(env,cell)) {
	if (!enif_get_list_cell(env, cell, &tuple, &cell)) return enif_make_badarg(env);
	if (enif_get_tuple(env,tuple,&arity,&pair)) {
	    enif_make_map_put(env, map, pair[0], pair[1], &map);
	}
    }

    return map;
}

static ERL_NIF_TERM sorted_list_from_maps_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {

    ERL_NIF_TERM map = argv[0];
    ERL_NIF_TERM list_f = enif_make_list(env, 0); /* NIL */
    ERL_NIF_TERM list_b = enif_make_list(env, 0); /* NIL */
    ERL_NIF_TERM key, value, k2, v2;
    ErlNifMapIterator iter_f;
    ErlNifMapIterator iter_b;
    int cnt, next_ret, prev_ret;

    if (argc != 1 && !enif_is_map(env, map))
	return enif_make_int(env, __LINE__);

    if(!enif_map_iterator_create(env, map, &iter_f, ERL_NIF_MAP_ITERATOR_FIRST))
	return enif_make_int(env, __LINE__);

    cnt = 0;
    next_ret = 1;
    while(enif_map_iterator_get_pair(env,&iter_f,&key,&value)) {
	if (!next_ret)
	    return enif_make_int(env, __LINE__);
	list_f = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_f);
	next_ret = enif_map_iterator_next(env,&iter_f);
	cnt++;
    }
    if (cnt && next_ret)
	return enif_make_int(env, __LINE__);

    if(!enif_map_iterator_create(env, map, &iter_b, ERL_NIF_MAP_ITERATOR_LAST))
	return enif_make_int(env, __LINE__);

    cnt = 0;
    prev_ret = 1;
    while(enif_map_iterator_get_pair(env,&iter_b,&key,&value)) {
	if (!prev_ret)
	    return enif_make_int(env, __LINE__);

	/* Test that iter_f can step "backwards" */
	if (!enif_map_iterator_prev(env,&iter_f)
	    || !enif_map_iterator_get_pair(env,&iter_f,&k2,&v2)
	    || k2 != key || v2 != value) {
	    return enif_make_int(env, __LINE__);
	}

	list_b = enif_make_list_cell(env, enif_make_tuple2(env, key, value), list_b);
	prev_ret = enif_map_iterator_prev(env,&iter_b);
	cnt++;
    }

    if (cnt) {
	if (prev_ret || enif_map_iterator_prev(env,&iter_f))
	    return enif_make_int(env, __LINE__);

	/* Test that iter_b can step "backwards" one step */
	if (!enif_map_iterator_next(env, &iter_b)
	    || !enif_map_iterator_get_pair(env,&iter_b,&k2,&v2)
	    || k2 != key || v2 != value)
	    return enif_make_int(env, __LINE__);
    }

    enif_map_iterator_destroy(env, &iter_f);
    enif_map_iterator_destroy(env, &iter_b);

    return enif_make_tuple2(env, list_f, list_b);
}


static ERL_NIF_TERM monotonic_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifTimeUnit time_unit;

    if (argc != 1)
	return atom_false;

    if (enif_compare(argv[0], atom_second) == 0)
	time_unit = ERL_NIF_SEC;
    else if (enif_compare(argv[0], atom_millisecond) == 0)
	time_unit = ERL_NIF_MSEC;
    else if (enif_compare(argv[0], atom_microsecond) == 0)
	time_unit = ERL_NIF_USEC;
    else if (enif_compare(argv[0], atom_nanosecond) == 0)
	time_unit = ERL_NIF_NSEC;
    else
	time_unit = 4711; /* invalid time unit */

    return enif_make_int64(env, enif_monotonic_time(time_unit));
}

static ERL_NIF_TERM time_offset(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifTimeUnit time_unit;

    if (argc != 1)
	return atom_false;

    if (enif_compare(argv[0], atom_second) == 0)
	time_unit = ERL_NIF_SEC;
    else if (enif_compare(argv[0], atom_millisecond) == 0)
	time_unit = ERL_NIF_MSEC;
    else if (enif_compare(argv[0], atom_microsecond) == 0)
	time_unit = ERL_NIF_USEC;
    else if (enif_compare(argv[0], atom_nanosecond) == 0)
	time_unit = ERL_NIF_NSEC;
    else
	time_unit = 4711; /* invalid time unit */
    return enif_make_int64(env, enif_time_offset(time_unit));
}

static ERL_NIF_TERM convert_time_unit(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifSInt64 i64;
    ErlNifTime val;
    ErlNifTimeUnit from, to;

    if (argc != 3)
	return atom_false;

    if (!enif_get_int64(env, argv[0], &i64))
	return enif_make_badarg(env);

    val = (ErlNifTime) i64;

    if (enif_compare(argv[1], atom_second) == 0)
	from = ERL_NIF_SEC;
    else if (enif_compare(argv[1], atom_millisecond) == 0)
	from = ERL_NIF_MSEC;
    else if (enif_compare(argv[1], atom_microsecond) == 0)
	from = ERL_NIF_USEC;
    else if (enif_compare(argv[1], atom_nanosecond) == 0)
	from = ERL_NIF_NSEC;
    else
	from = 4711; /* invalid time unit */

    if (enif_compare(argv[2], atom_second) == 0)
	to = ERL_NIF_SEC;
    else if (enif_compare(argv[2], atom_millisecond) == 0)
	to = ERL_NIF_MSEC;
    else if (enif_compare(argv[2], atom_microsecond) == 0)
	to = ERL_NIF_USEC;
    else if (enif_compare(argv[2], atom_nanosecond) == 0)
	to = ERL_NIF_NSEC;
    else
	to = 4711; /* invalid time unit */

    return enif_make_int64(env, enif_convert_time_unit(val, from, to));
}

static ERL_NIF_TERM now_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    return enif_now_time(env);
}

static ERL_NIF_TERM cpu_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    return enif_cpu_time(env);
}

static ERL_NIF_TERM unique_integer(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM atom_pos = enif_make_atom(env,"positive"),
        atom_mon = enif_make_atom(env,"monotonic");
    ERL_NIF_TERM opts = argv[0], opt;
    ErlNifUniqueInteger properties = 0;

    while (!enif_is_empty_list(env, opts)) {
	if (!enif_get_list_cell(env, opts, &opt, &opts))
            return enif_make_badarg(env);

        if (enif_compare(opt, atom_pos) == 0)
            properties |= ERL_NIF_UNIQUE_POSITIVE;
        if (enif_compare(opt, atom_mon) == 0)
            properties |= ERL_NIF_UNIQUE_MONOTONIC;
    }

    return enif_make_unique_integer(env, properties);
}

static ERL_NIF_TERM is_process_alive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifPid pid;
    if (!enif_get_local_pid(env, argv[0], &pid))
        return enif_make_badarg(env);
    if (enif_is_process_alive(env, &pid))
        return atom_true;
    return atom_false;
}

static ERL_NIF_TERM is_port_alive(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifPort port;
    if (!enif_get_local_port(env, argv[0], &port))
        return enif_make_badarg(env);
    if (enif_is_port_alive(env, &port))
        return atom_true;
    return atom_false;
}

static ERL_NIF_TERM term_to_binary(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary bin;
    ErlNifPid pid;
    ErlNifEnv *msg_env = env;
    ERL_NIF_TERM term;

    if (enif_get_local_pid(env, argv[1], &pid))
        msg_env = enif_alloc_env();

    if (!enif_term_to_binary(msg_env, argv[0], &bin))
        return enif_make_badarg(env);

    term = enif_make_binary(msg_env, &bin);

    if (msg_env != env) {
        enif_send(env, &pid, msg_env, term);
        enif_free_env(msg_env);
        return atom_true;
    } else {
        return term;
    }
}

static ERL_NIF_TERM binary_to_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary bin;
    ERL_NIF_TERM term, ret_term;
    ErlNifPid pid;
    ErlNifEnv *msg_env = env;
    unsigned int opts;
    ErlNifUInt64 ret;

    if (enif_get_local_pid(env, argv[1], &pid))
        msg_env = enif_alloc_env();

    if (!enif_inspect_binary(env, argv[0], &bin)
	|| !enif_get_uint(env, argv[2], &opts))
        return enif_make_badarg(env);

    ret = enif_binary_to_term(msg_env, bin.data, bin.size, &term,
			      (ErlNifBinaryToTerm)opts);
    if (!ret)
	return atom_false;

    ret_term = enif_make_uint64(env, ret);
    if (msg_env != env) {
        enif_send(env, &pid, msg_env, term);
        enif_free_env(msg_env);
        return ret_term;
    } else {
        return enif_make_tuple2(env, ret_term, term);
    }
}

static ERL_NIF_TERM port_command(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifPort port;

    if (!enif_get_local_port(env, argv[0], &port))
        return enif_make_badarg(env);

    if (!enif_port_command(env, &port, NULL, argv[1]))
        return enif_make_badarg(env);
    return atom_true;
}

static ERL_NIF_TERM format_term(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ErlNifBinary obin;
    unsigned int size;

    if (!enif_get_uint(env, argv[0], &size))
	return enif_make_badarg(env);
    if (!enif_alloc_binary(size,&obin))
	return enif_make_badarg(env);

    if (enif_snprintf((char*)obin.data, (size_t)size, "%T", argv[1]) < 0)
        return atom_false;

    return enif_make_binary(env,&obin);
}


static int get_fd(ErlNifEnv* env, ERL_NIF_TERM term, struct fd_resource** rsrc)
{
    if (!enif_get_resource(env, term, fd_resource_type, (void**)rsrc)) {
        return 0;
    }
    return 1;
}

static ERL_NIF_TERM select_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    struct fd_resource* fdr;
    enum ErlNifSelectFlags mode;
    void* obj;
    ErlNifPid nifpid, *pid = NULL;
    ERL_NIF_TERM ref;
    enum ErlNifSelectReturn retval;

    if (!get_fd(env, argv[0], &fdr)
        || !enif_get_uint(env, argv[1], &mode)
        || !enif_get_resource(env, argv[2], fd_resource_type, &obj))
    {
        return enif_make_badarg(env);
    }

    if (argv[3] != atom_null) {
	if (!enif_get_local_pid(env, argv[3], &nifpid))
	    return enif_make_badarg(env);
	pid = &nifpid;
    }
    ref = argv[4];

    fdr->was_selected = 1;
    enif_self(env, &fdr->pid);
    retval = enif_select(env, fdr->fd, mode, obj, pid, ref);

    return enif_make_int(env, (int)retval);
}

static ERL_NIF_TERM pipe_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    struct fd_resource* read_rsrc;
    struct fd_resource* write_rsrc;
    ERL_NIF_TERM read_fd, write_fd;
    int fds[2], flags;

    if (pipe(fds) < 0)
        return enif_make_string(env, "pipe failed", ERL_NIF_LATIN1);

    if ((flags = fcntl(fds[0], F_GETFL, 0)) < 0
        || fcntl(fds[0], F_SETFL, flags|O_NONBLOCK) < 0
        || (flags = fcntl(fds[1], F_GETFL, 0)) < 0
        || fcntl(fds[1], F_SETFL, flags|O_NONBLOCK) < 0) {
        close(fds[0]);
        close(fds[1]);
        return enif_make_string(env, "fcntl failed on pipe", ERL_NIF_LATIN1);
    }

    read_rsrc  = enif_alloc_resource(fd_resource_type, sizeof(struct fd_resource));
    write_rsrc = enif_alloc_resource(fd_resource_type, sizeof(struct fd_resource));
    read_rsrc->fd  = fds[0];
    read_rsrc->was_selected = 0;
    write_rsrc->fd = fds[1];
    write_rsrc->was_selected = 0;
    read_fd  = enif_make_resource(env, read_rsrc);
    write_fd = enif_make_resource(env, write_rsrc);
    enif_release_resource(read_rsrc);
    enif_release_resource(write_rsrc);

    return enif_make_tuple2(env,
               enif_make_tuple2(env, read_fd, make_pointer(env, read_rsrc)),
               enif_make_tuple2(env, write_fd, make_pointer(env, write_rsrc)));
}

static ERL_NIF_TERM write_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    struct fd_resource* fdr;
    ErlNifBinary bin;
    int n, written = 0;

    if (!get_fd(env, argv[0], &fdr)
        || !enif_inspect_binary(env, argv[1], &bin))
        return enif_make_badarg(env);

    for (;;) {
        n = write(fdr->fd, bin.data + written, bin.size - written);
        if (n >= 0) {
            written += n;
            if (written == bin.size) {
                return atom_ok;
            }
        }
        else if (errno == EAGAIN) {
            return enif_make_tuple2(env, atom_eagain, enif_make_int(env, written));
        }
        else if (errno == EINTR) {
            continue;
        }
        else {
            return enif_make_tuple2(env, atom_error, enif_make_int(env, errno));
        }
    }
}

static ERL_NIF_TERM read_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    struct fd_resource* fdr;
    unsigned char* buf;
    int n, count;
    ERL_NIF_TERM res;

    if (!get_fd(env, argv[0], &fdr)
        || !enif_get_int(env, argv[1], &count) || count < 1)
        return enif_make_badarg(env);

    buf = enif_make_new_binary(env, count, &res);

    for (;;) {
        n = read(fdr->fd, buf, count);
        if (n > 0) {
            if (n < count) {
                res = enif_make_sub_binary(env, res, 0, n);
            }
            return res;
        }
        else if (n == 0) {
            return atom_eof;
        }
        else if (errno == EAGAIN) {
            return atom_eagain;
        }
        else if (errno == EINTR) {
            continue;
        }
        else {
            return enif_make_tuple2(env, atom_error, enif_make_int(env, errno));
        }
    }
}

static ERL_NIF_TERM is_closed_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    struct fd_resource* fdr;

    if (!get_fd(env, argv[0], &fdr))
        return enif_make_badarg(env);

    return fdr->fd < 0 ? atom_true : atom_false;
}

static void fd_resource_dtor(ErlNifEnv* env, void* obj)
{
    struct fd_resource* fdr = (struct fd_resource*)obj;
    resource_dtor(env, obj);
    if (fdr->fd >= 0) {
        assert(!fdr->was_selected);
        close(fdr->fd);
    }
}

static struct {
    void* obj;
    int was_direct_call;
}last_fd_stop;
int fd_stop_cnt = 0;

static void fd_resource_stop(ErlNifEnv* env, void* obj, ErlNifEvent fd,
                             int is_direct_call)
{
    struct fd_resource* fdr = (struct fd_resource*)obj;
    assert(fd == fdr->fd);
    assert(fd >= 0);

    last_fd_stop.obj = obj;
    last_fd_stop.was_direct_call = is_direct_call;
    fd_stop_cnt++;

    close(fd);
    fdr->fd = -1;   /* thread safety ? */
    fdr->was_selected = 0;

    {
        ErlNifEnv* msg_env = enif_alloc_env();
        ERL_NIF_TERM msg;
        msg = enif_make_tuple3(msg_env,
                               atom_fd_resource_stop,
                               make_pointer(msg_env, obj),
                               enif_make_int(msg_env, is_direct_call));

        enif_send(env, &fdr->pid, msg_env, msg);
        enif_free_env(msg_env);
    }
}

static ERL_NIF_TERM last_fd_stop_call(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    ERL_NIF_TERM last, ret;
    last = enif_make_tuple2(env, make_pointer(env, last_fd_stop.obj),
                            enif_make_int(env, last_fd_stop.was_direct_call));
    ret = enif_make_tuple2(env, enif_make_int(env, fd_stop_cnt), last);
    fd_stop_cnt = 0;
    return ret;
}


static void monitor_resource_dtor(ErlNifEnv* env, void* obj)
{
    resource_dtor(env, obj);
}

static ERL_NIF_TERM make_monitor(ErlNifEnv* env, const ErlNifMonitor* mon)
{
    ERL_NIF_TERM mon_bin;
    memcpy(enif_make_new_binary(env, sizeof(ErlNifMonitor), &mon_bin),
           mon, sizeof(ErlNifMonitor));
    return mon_bin;
}

static int get_monitor(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifMonitor* mon)
{
    ErlNifBinary bin;
    if (!enif_inspect_binary(env, term, &bin)
        || bin.size != sizeof(ErlNifMonitor))
        return 0;
    memcpy(mon, bin.data, bin.size);
    return 1;
}

static void monitor_resource_down(ErlNifEnv* env, void* obj, ErlNifPid* pid,
                                  ErlNifMonitor* mon)
{
    struct monitor_resource* rsrc = (struct monitor_resource*)obj;
    ErlNifEnv* build_env;
    ErlNifEnv* msg_env;
    ERL_NIF_TERM msg;

    if (rsrc->use_msgenv) {
        msg_env = enif_alloc_env();
        build_env = msg_env;
    }
    else {
        msg_env = NULL;
        build_env = env;
    }

    msg = enif_make_tuple4(build_env,
                           atom_monitor_resource_down,
                           make_pointer(build_env, obj),
                           enif_make_pid(build_env, pid),
                           make_monitor(build_env, mon));

    enif_send(env, &rsrc->receiver, msg_env, msg);
    if (msg_env)
        enif_free_env(msg_env);
}

static ERL_NIF_TERM alloc_monitor_resource_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    struct monitor_resource* rsrc;

    rsrc  = enif_alloc_resource(monitor_resource_type, sizeof(struct monitor_resource));

    return make_pointer(env,rsrc);
}

static ERL_NIF_TERM monitor_process_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    struct monitor_resource* rsrc;
    ErlNifPid target;
    ErlNifMonitor mon;
    int res;

    if (!get_pointer(env, argv[0], (void**)&rsrc)
        || !enif_get_local_pid(env, argv[1], &target)
        || !enif_get_local_pid(env, argv[3], &rsrc->receiver)) {
        return enif_make_badarg(env);
    }

    rsrc->use_msgenv = (argv[2] == atom_true);
    res = enif_monitor_process(env, rsrc, &target, &mon);

    return enif_make_tuple2(env, enif_make_int(env, res), make_monitor(env, &mon));
}

static ERL_NIF_TERM demonitor_process_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    struct monitor_resource* rsrc;
    ErlNifMonitor mon;
    int res;

    if (!get_pointer(env, argv[0], (void**)&rsrc)
        || !get_monitor(env, argv[1], &mon)) {
        return enif_make_badarg(env);
    }

    res = enif_demonitor_process(env, rsrc, &mon);

    return enif_make_int(env, res);
}

/*********** monitor_frenzy ************/

struct frenzy_rand_bits
{
    unsigned int source;
    unsigned int bits_consumed;
};

static unsigned int frenzy_rand_bits_max;

unsigned rand_bits(struct frenzy_rand_bits* rnd, unsigned int nbits)
{
    unsigned int res;

    rnd->bits_consumed += nbits;
    assert(rnd->bits_consumed <= frenzy_rand_bits_max);
    res = rnd->source & ((1 << nbits)-1);
    rnd->source >>= nbits;
    return res;
}

#define FRENZY_PROCS_MAX_BITS 4
#define FRENZY_PROCS_MAX (1 << FRENZY_PROCS_MAX_BITS)

#define FRENZY_RESOURCES_MAX_BITS 4
#define FRENZY_RESOURCES_MAX (1 << FRENZY_RESOURCES_MAX_BITS)

#define FRENZY_MONITORS_MAX_BITS 4
#define FRENZY_MONITORS_MAX (1 << FRENZY_MONITORS_MAX_BITS)

struct frenzy_monitor {
    ErlNifMutex* lock;
    enum { 
        MON_FREE, MON_FREE_DOWN, MON_FREE_DEMONITOR,
        MON_TRYING, MON_ACTIVE, MON_PENDING
    } state;
    ErlNifMonitor mon;
    ErlNifPid pid;
    unsigned int use_cnt;
};

struct frenzy_resource {
    unsigned int rix;
    struct frenzy_monitor monv[FRENZY_MONITORS_MAX];
};
struct frenzy_reslot {
    ErlNifMutex* lock;
    struct frenzy_resource* obj;
    unsigned long alloc_cnt;
    unsigned long release_cnt;
};
static struct frenzy_reslot resv[FRENZY_RESOURCES_MAX];

static ERL_NIF_TERM monitor_frenzy_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
{
    struct frenzy_proc {
        ErlNifPid pid;
        int is_free;
    };
    static struct frenzy_proc procs[FRENZY_PROCS_MAX];
    static struct frenzy_proc* proc_refs[FRENZY_PROCS_MAX];
    static unsigned int nprocs, old_nprocs;
    static ErlNifMutex* procs_lock;
    static unsigned long spawn_cnt = 0;
    static unsigned long kill_cnt = 0;
    static unsigned long proc_histogram[FRENZY_PROCS_MAX];

    static const unsigned int primes[] = {7, 13, 17, 19};

    struct frenzy_resource* r;
    struct frenzy_rand_bits rnd;
    unsigned int op, inc, my_nprocs;
    unsigned int mix;  /* r->monv[] index */
    unsigned int rix;  /* resv[] index */
    unsigned int pix;  /* procs[] index */
    unsigned int ref_ix; /* proc_refs[] index */
    int self_pix, rv;
    ERL_NIF_TERM retval = atom_error;
    const ERL_NIF_TERM Op = argv[0];
    const ERL_NIF_TERM Rnd = argv[1];
    const ERL_NIF_TERM SelfPix = argv[2];
    const ERL_NIF_TERM NewPid = argv[3];

    if (enif_is_atom(env, Op)) {
        if (Op == atom_init) {
            if (procs_lock || !enif_get_uint(env, Rnd, &frenzy_rand_bits_max))
                return enif_make_badarg(env);

            procs_lock = enif_mutex_create("nif_SUITE:monitor_frenzy.procs");
            nprocs = 0;
            old_nprocs = 0;
            for (pix = 0; pix < FRENZY_PROCS_MAX; pix++) {
                proc_refs[pix] = &procs[pix];
                procs[pix].is_free = 1;
                proc_histogram[pix] = 0;
            }
            for (rix = 0; rix < FRENZY_RESOURCES_MAX; rix++) {
                resv[rix].lock = enif_mutex_create("nif_SUITE:monitor_frenzy.resv.lock");
                resv[rix].obj = NULL;
                resv[rix].alloc_cnt = 0;
                resv[rix].release_cnt = 0;
            }

            /* Add self as first process */
            enif_self(env, &procs[0].pid);
            procs[0].is_free = 0;
            old_nprocs = ++nprocs;

            spawn_cnt = 1;
            kill_cnt = 0;
            return enif_make_uint(env, 0);  /* SelfPix */
        }
        else if (Op == atom_stats) {
            ERL_NIF_TERM hist[FRENZY_PROCS_MAX];
            unsigned long res_alloc_cnt = 0;
            unsigned long res_release_cnt = 0;
            for (ref_ix = 0; ref_ix < FRENZY_PROCS_MAX; ref_ix++) {
                hist[ref_ix] = enif_make_ulong(env, proc_histogram[ref_ix]);
            }
            for (rix = 0; rix < FRENZY_RESOURCES_MAX; rix++) {
                res_alloc_cnt += resv[rix].alloc_cnt;
                res_release_cnt += resv[rix].release_cnt;
            }

            return
            enif_make_list4(env,
                            enif_make_tuple2(env, enif_make_string(env, "proc_histogram", ERL_NIF_LATIN1),
                                             enif_make_list_from_array(env, hist, FRENZY_PROCS_MAX)),
                            enif_make_tuple2(env, enif_make_string(env, "spawn_cnt", ERL_NIF_LATIN1),
                                             enif_make_ulong(env, spawn_cnt)),
                            enif_make_tuple2(env, enif_make_string(env, "kill_cnt", ERL_NIF_LATIN1),
                                             enif_make_ulong(env, kill_cnt)),
                            enif_make_tuple3(env, enif_make_string(env, "resource_alloc", ERL_NIF_LATIN1),
                                             enif_make_ulong(env, res_alloc_cnt),
                                             enif_make_ulong(env, res_release_cnt)));

        }
        else if (Op == atom_stop && procs_lock) {  /* stop all */
            retval = enif_make_list(env, 0);
            enif_mutex_lock(procs_lock);
            for (ref_ix = 0; ref_ix < nprocs; ref_ix++) {
                assert(!proc_refs[ref_ix]->is_free);
                retval = enif_make_list_cell(env, enif_make_pid(env, &proc_refs[ref_ix]->pid),
                                             retval);
                proc_refs[ref_ix]->is_free = 1;
            }
            kill_cnt += nprocs;
            nprocs = 0;
            old_nprocs = 0;
            enif_mutex_unlock(procs_lock);
            return retval;
        }
        return enif_make_badarg(env);
    }

    if (!enif_get_int(env, SelfPix, &self_pix) ||
        !enif_get_uint(env, Op, &op) ||
        !enif_get_uint(env, Rnd, &rnd.source))
        return enif_make_badarg(env);

    rnd.bits_consumed = 0;
    switch (op) {
    case 0:  { /* add/remove process */
        ErlNifPid self;
        enif_self(env, &self);

        ref_ix = rand_bits(&rnd, FRENZY_PROCS_MAX_BITS) % FRENZY_PROCS_MAX;
        enif_mutex_lock(procs_lock);
        if (procs[self_pix].is_free || procs[self_pix].pid.pid != self.pid) {
            /* Some one already removed me */
            enif_mutex_unlock(procs_lock);
            return atom_done;
        }
        if (ref_ix >= nprocs || nprocs < 2) { /* add process */
            ref_ix = nprocs++;
            pix = proc_refs[ref_ix] - procs;
            assert(procs[pix].is_free);
            if (!enif_get_local_pid(env, NewPid, &procs[pix].pid))
                abort();
            procs[pix].is_free = 0;
            spawn_cnt++;
            proc_histogram[ref_ix]++;
            old_nprocs = nprocs;
            enif_mutex_unlock(procs_lock);
            DBG_TRACE2("Add pid %T, nprocs = %u\n", NewPid, nprocs);
            retval = enif_make_uint(env, pix);
        }
        else { /* remove process */
            pix = proc_refs[ref_ix] - procs;
            if (pix == self_pix) {
                ref_ix = (ref_ix + 1) % nprocs;
                pix = proc_refs[ref_ix] - procs;
            }
            assert(procs[pix].pid.pid != self.pid);
            assert(!procs[pix].is_free);
            retval = enif_make_pid(env, &procs[pix].pid);
            --nprocs;
            assert(!proc_refs[nprocs]->is_free);
            if (ref_ix != nprocs) {
                struct frenzy_proc* tmp = proc_refs[ref_ix];
                proc_refs[ref_ix] = proc_refs[nprocs];
                proc_refs[nprocs] = tmp;
            }
            procs[pix].is_free = 1;
            proc_histogram[nprocs]++;
            kill_cnt++;
            enif_mutex_unlock(procs_lock);
            DBG_TRACE2("Removed pid %T, nprocs = %u\n", retval, nprocs);
        }
	break;
    }
    case 1:
    case 2: /* create/delete/lookup resource */
	rix = rand_bits(&rnd, FRENZY_RESOURCES_MAX_BITS) % FRENZY_RESOURCES_MAX;
	inc = primes[rand_bits(&rnd, 2)];
	while (enif_mutex_trylock(resv[rix].lock) == EBUSY) {
	    rix = (rix + inc) % FRENZY_RESOURCES_MAX;
	}
	if (resv[rix].obj == NULL) {
	    r = enif_alloc_resource(frenzy_resource_type,
				    sizeof(struct frenzy_resource));
	    resv[rix].obj = r;
            resv[rix].alloc_cnt++;
	    r->rix = rix;
            for (mix = 0; mix < FRENZY_MONITORS_MAX; mix++) {
                r->monv[mix].lock = enif_mutex_create("nif_SUITE:monitor_frenzy.monv.lock");
                r->monv[mix].state = MON_FREE;
                r->monv[mix].use_cnt = 0;
                r->monv[mix].pid.pid = 0; /* null-pid */
            }
            DBG_TRACE2("New resource at r=%p rix=%u\n", r, rix);
	}
	else if (rand_bits(&rnd, 3) == 0) {
            r = resv[rix].obj;
            resv[rix].obj = NULL;
            resv[rix].release_cnt++;
	    enif_mutex_unlock(resv[rix].lock);
            DBG_TRACE2("Delete resource at r=%p rix=%u\n", r, rix);
	    enif_release_resource(r);
	    retval = atom_ok;
	    break;
	}
	else {
	    r = resv[rix].obj;
	}
        enif_keep_resource(r);
        enif_mutex_unlock(resv[rix].lock);

        /* monitor/demonitor */

        mix = rand_bits(&rnd, FRENZY_MONITORS_MAX_BITS) % FRENZY_MONITORS_MAX;
        inc = primes[rand_bits(&rnd, 2)];
        while (enif_mutex_trylock(r->monv[mix].lock) == EBUSY) {
            mix = (mix + inc) % FRENZY_MONITORS_MAX;
        }
        switch (r->monv[mix].state) {
        case MON_FREE:
        case MON_FREE_DOWN:
        case MON_FREE_DEMONITOR: {   /* do monitor */
            /*
             * Use an old possibly larger value of 'nprocs', to increase
             * probability of monitoring an already terminated process
             */
            my_nprocs = old_nprocs;
            if (my_nprocs > 0) {
                int save_state = r->monv[mix].state;
                ref_ix = rand_bits(&rnd, FRENZY_PROCS_MAX_BITS) % my_nprocs;
                pix = proc_refs[ref_ix] - procs;
                r->monv[mix].pid.pid = procs[pix].pid.pid; /* "atomic" */
                r->monv[mix].state = MON_TRYING;
                rv = enif_monitor_process(env, r, &r->monv[mix].pid, &r->monv[mix].mon);
                if (rv == 0) {
                    r->monv[mix].state = MON_ACTIVE;
                    r->monv[mix].use_cnt++;
                    DBG_TRACE3("Monitor from r=%p rix=%u to %T\n",
                                 r, r->rix, r->monv[mix].pid.pid);
                }
                else {
                    r->monv[mix].state = save_state;
                    DBG_TRACE4("Monitor from r=%p rix=%u to %T FAILED with %d\n",
                               r, r->rix, r->monv[mix].pid.pid, rv);
                }
                retval = enif_make_int(env,rv);
            }
            else {
                DBG_TRACE0("No pids to monitor\n");
                retval = atom_ok;
            }
            break;
        }
        case MON_ACTIVE: /* do demonitor */
            rv = enif_demonitor_process(env, r, &r->monv[mix].mon);
            if (rv == 0) {
                DBG_TRACE3("Demonitor from r=%p rix=%u to %T\n",
                           r, r->rix, r->monv[mix].pid.pid);
                r->monv[mix].state = MON_FREE_DEMONITOR;
            }
            else {
                DBG_TRACE4("Demonitor from r=%p rix=%u to %T FAILED with %d\n",
                           r, r->rix, r->monv[mix].pid.pid, rv);
                r->monv[mix].state = MON_PENDING;
            }
            retval = enif_make_int(env,rv);
            break;

        case MON_PENDING: /* waiting for 'down' callback, do nothing */
            retval = atom_ok;
            break;
        default:
            abort();
            break;
        }
        enif_mutex_unlock(r->monv[mix].lock);
        enif_release_resource(r);
	break;

    case 3: /* no-op */
        retval = atom_ok;
        break;
    }

    {
        int percent = (rand_bits(&rnd, 6) + 1) * 2;  /* 2 to 128 */
        if (percent <= 100)
            enif_consume_timeslice(env, percent);
    }

    return retval;
}

static void frenzy_resource_dtor(ErlNifEnv* env, void* obj)
{
    struct frenzy_resource* r = (struct frenzy_resource*) obj;
    unsigned int mix;

    DBG_TRACE2("DTOR r=%p rix=%u\n", r, r->rix);

    for (mix = 0; mix < FRENZY_MONITORS_MAX; mix++) {
        assert(r->monv[mix].state != MON_PENDING);
        enif_mutex_destroy(r->monv[mix].lock);
        r->monv[mix].lock = NULL;
    }

}

static void frenzy_resource_down(ErlNifEnv* env, void* obj, ErlNifPid* pid,
                                 ErlNifMonitor* mon)
{
    struct frenzy_resource* r = (struct frenzy_resource*) obj;
    unsigned int mix;

    DBG_TRACE3("DOWN pid=%T, r=%p rix=%u\n", pid->pid, r, r->rix);

    for (mix = 0; mix < FRENZY_MONITORS_MAX; mix++) {
        if (r->monv[mix].pid.pid == pid->pid && r->monv[mix].state >= MON_TRYING) {
            enif_mutex_lock(r->monv[mix].lock);
            if (memcmp(mon, &r->monv[mix].mon, sizeof(*mon)) == 0) {
                assert(r->monv[mix].state >= MON_ACTIVE);
                r->monv[mix].state = MON_FREE_DOWN;
                enif_mutex_unlock(r->monv[mix].lock);
                return;
            }
            enif_mutex_unlock(r->monv[mix].lock);
        }
    }
    enif_fprintf(stderr, "DOWN called for unknown monitor\n");
    abort();
}



static ErlNifFunc nif_funcs[] =
{
    {"lib_version", 0, lib_version},
    {"call_history", 0, call_history},
    {"hold_nif_mod_priv_data", 1, hold_nif_mod_priv_data},
    {"nif_mod_call_history", 0, nif_mod_call_history},
    {"list_seq", 1, list_seq},
    {"type_test", 0, type_test},
    {"tuple_2_list", 1, tuple_2_list},
    {"is_identical",2,is_identical},
    {"compare",2,compare},
    {"many_args_100", 100, many_args_100},
    {"clone_bin", 1, clone_bin},
    {"make_sub_bin", 3, make_sub_bin},
    {"string_to_bin", 2, string_to_bin},
    {"atom_to_bin", 2, atom_to_bin},
    {"macros", 1, macros},
    {"tuple_2_list_and_tuple",1,tuple_2_list_and_tuple},
    {"iolist_2_bin", 1, iolist_2_bin},
    {"get_resource_type", 1, get_resource_type},
    {"alloc_resource", 2, alloc_resource},
    {"make_resource", 1, make_resource},
    {"get_resource", 2, get_resource},
    {"release_resource", 1, release_resource},
    {"last_resource_dtor_call", 0, last_resource_dtor_call},
    {"make_new_resource", 2, make_new_resource},
    {"check_is", 11, check_is},
    {"check_is_exception", 0, check_is_exception},
    {"length_test", 6, length_test},
    {"make_atoms", 0, make_atoms},
    {"make_strings", 0, make_strings},
    {"make_new_resource", 2, make_new_resource},
    {"make_new_resource_binary", 1, make_new_resource_binary},
    {"send_list_seq", 2, send_list_seq},
    {"send_new_blob", 2, send_new_blob},
    {"alloc_msgenv", 0, alloc_msgenv},
    {"clear_msgenv", 1, clear_msgenv},
    {"grow_blob", 2, grow_blob},
    {"grow_blob", 3, grow_blob},
    {"send_blob", 2, send_blob},
    {"send3_blob", 3, send3_blob},
    {"send_blob_thread", 3, send_blob_thread},
    {"join_send_thread", 1, join_send_thread},
    {"copy_blob", 1, copy_blob},
    {"send_term", 2, send_term},
    {"send_copy_term", 2, send_copy_term},
    {"reverse_list",1, reverse_list},
    {"echo_int", 1, echo_int},
    {"type_sizes", 0, type_sizes},
    {"otp_9668_nif", 1, otp_9668_nif},
    {"otp_9828_nif", 1, otp_9828_nif},
    {"consume_timeslice_nif", 2, consume_timeslice_nif},
    {"call_nif_schedule", 2, call_nif_schedule},
    {"call_nif_exception", 1, call_nif_exception},
    {"call_nif_nan_or_inf", 1, call_nif_nan_or_inf},
    {"call_nif_atom_too_long", 1, call_nif_atom_too_long},
    {"is_map_nif", 1, is_map_nif},
    {"get_map_size_nif", 1, get_map_size_nif},
    {"make_new_map_nif", 0, make_new_map_nif},
    {"make_map_put_nif", 3, make_map_put_nif},
    {"get_map_value_nif", 2, get_map_value_nif},
    {"make_map_update_nif", 3, make_map_update_nif},
    {"make_map_remove_nif", 2, make_map_remove_nif},
    {"maps_from_list_nif", 1, maps_from_list_nif},
    {"sorted_list_from_maps_nif", 1, sorted_list_from_maps_nif},
    {"monotonic_time", 1, monotonic_time},
    {"time_offset", 1, time_offset},
    {"convert_time_unit", 3, convert_time_unit},
    {"now_time", 0, now_time},
    {"cpu_time", 0, cpu_time},
    {"unique_integer_nif", 1, unique_integer},
    {"is_process_alive_nif", 1, is_process_alive},
    {"is_port_alive_nif", 1, is_port_alive},
    {"term_to_binary_nif", 2, term_to_binary},
    {"binary_to_term_nif", 3, binary_to_term},
    {"port_command_nif", 2, port_command},
    {"format_term_nif", 2, format_term},
    {"select_nif", 5, select_nif},
    {"pipe_nif", 0, pipe_nif},
    {"write_nif", 2, write_nif},
    {"read_nif", 2, read_nif},
    {"is_closed_nif", 1, is_closed_nif},
    {"last_fd_stop_call", 0, last_fd_stop_call},
    {"alloc_monitor_resource_nif", 0, alloc_monitor_resource_nif},
    {"monitor_process_nif", 4, monitor_process_nif},
    {"demonitor_process_nif", 2, demonitor_process_nif},
    {"monitor_frenzy_nif", 4, monitor_frenzy_nif}
};

ERL_NIF_INIT(nif_SUITE,nif_funcs,load,NULL,upgrade,unload)