ToC | DocOverview | CGDoc | RelNotes | FAQ | Index | PermutedIndex
| Allegro CL version 10.1 Unrevised from 10.0 to 10.1. 10.0 version

# User-defined main()

This document contains the following sections:

1.0 Introduction
2.0 On UNIX
2.1 Remarks on main() on Unix
3.0 On Windows
3.1 Remarks on main() on Windows
Appendix A. Potential problems with user-defined main() on Linux

## 1.0 Introduction

Allegro CL consists of three standard parts and one optional part:

1. The Lisp main, an executable program. The responsibility of the main is to gather appropriate information and to call lisp_init with that information.
2. The Allegro CL Shared Library. This is a .so or .sl file on Unix and a .dll file on Windows (usually libaclnnn.so/sl on Unix and aclnnn.dll on Windows, where nnn are digits and letters -- perhaps more than 3). It contains lisp_init and all of the runtime system that is needed to build or attach the Lisp heap.
3. The Lisp heap or image. This is a .dxl file (as created by excl:build-lisp-image or excl:dumplisp) that is mapped or copied into memory and which provides the majority of Lisp and application functionality.
4. The optional purespace file. When used, this is a .pll file that provides sharable, read-only treatment of constant strings and codevectors.

Any Unix or Windows program may serve as a Lisp main program if it:

1. Gathers up proper information about the locations of all necessary files;
2. Links or dynamically loads the Allegro Shared Library; and
3. Calls lisp_init() with the appropriate arguments before trying to call any Lisp functionality.

Linking the Allegro CL Shared Library is discussed in this document. Dynamically loading the Allegro CL Shared Library is more complicated and operating-system dependent and is not further discussed. Please contact Franz Inc. for more information if you are interested in that option (see introduction.htm).

Basic requisite code for a minimal Lisp main is given in the following sections. The main should be linked with the Allegro CL Shared Library and with any other shared or static libraries that are required for Allegro CL and application operation.

## 2.0 On UNIX

In the example below, the [version] in libacl[version].so is different for each release of Allegro CL. Find the correct name by looking in the Allegro directory.

    struct shlib_library_item {
      char *    name;
      int        system;
    };

    #include <stdio.h>
    #include "misc/shlibs.h"

    void my_exit( int );

    main( int argc, char **argv, char **envp )
    {

      /* put custom installation code here */
      fprintf(stderr, "custom code runs..\n" );

      lisp_init(
      /* these three arguments are the main() arguments */
                argc, argv, envp,
      /* custom exit function; use 0 if default exit() call desired */
                my_exit,
      /* directory containing Allegro CL shared library and Lisp image(s ) */
                "/room2/test/src",
      /* Allegro CL shared library name; name could vary, so check your Allegro CL
         directory for the correct name */
                "libacl[version].so",
      /* Allegro CL shared library handle or 0 if none, 0 for this use */
                0,
      /* default lisp image name; use 0 if no default desired */
                "lisp.dxl",
      /* tls_slot_index 0 or 1, if 1 asks the Acldll to create its own
         thread-local index. See thread-local note below. This argument
         was 'quiet' in releases prior to 8.1 but not used. */
                0,
      /* Win32app flag; on UNIX, must be 0 */
                0,
      /* global variable containing prelinked library list;
         defined by shlibs.h include file */
                linked_shared_libraries );

    }

    void
    my_exit( int n )
    {
      fprintf( stderr, "my exit code can run here\n" );
      exit( n );
    }

### Thread-local note

As of 8.1, lisp thread-local values will be stored directly in the threads, rather than in the global table as they now are (thus requiring them to be moved in and out as threads are switched). This requires that Allegro CL be able to get one thread-local slot from the operating system (because thread-local storage is limited in count on some systems, we only require that one slot be provided, and we then manage that slot in the lisp). Some systems don't even provide for thread-local slots, or their provision has not been exploited yet; these systems are marked with `:no-os-tls` in the `*features*` list (this name means "no operating-system supplied thread-local storage"). These architectures can supply a 0 or a 1 to the tls_slot_index argument.

Also, most architectures can get a tls slot from within the acl shared library, though on some architectures there are very strict linking rules needed in order to do so. On the HP 64-bit port, which has tls, but whose shared-library cannot generate a tls slot, the tls_slot_index function pointer must be passed in.

The example is given in examples/linkacl/fact.c (on Unix machines, related example in examples/dll/fact.c on Windows). For HP, and for any other architecture which supports the relatively new __thread construct in C/C++, the code would look something like this:

    #ifdef NeedTls
    __thread nat tls_dummy_slot;

    nat
    tls_slot_index()
    {
    	return (nat)&tls_dummy_slot;
    }
    #endif

    and in the actual lisp_init, call where the quiet flag used to be:

        lisp_init( lnk_argc, lnk_argv, lnk_envp,  /* argc, argv, envp */
    	       0,  /* exit routine */
    	       dir_buf, /* system directory */
    	       (char*)LIBACL_NAME,
    	       0,
    	       (char*)"fact.dxl",  /* image name */
    #ifdef NeedTls
    	       &tls_slot_index,
    #else
    	       (nat(*)())1, /* quiet flag (old) */
    #endif
    	       0, /* unused on Unix */
    	       linked_shared_libraries );

For windows, the definition of tls_slot_index would be

    nat
    tls_slot_index()
    {
    	return ((nat)(TlsAlloc()));
    }

## 2.1 Remarks on main() on Unix

1. You most likely will not want to hardwire a pathname such as /room2/test/src. For example, you may require the user to set an environmental variable specifying the directory containing the required shared library and image; you can then call getenv() to find the needed value.
2. The default image name is used when there is no command line -I image name specified.
3. The linked_shared_libraries variable is needed for profiling, disassembly, and foreign function support. If you link your main program with additional shared libraries, add them to the shlibs.h file found in the misc subdirectory. For libraries that you add, set the "system" structure member to 0.
4. The lisp_init() function is found in the Allegro CL shared library. After you have successfully compiled and linked your custom executable, users will have to add the directory containing the Allegro CL shared library to their environmental variable containing the directories to be searched when a dynamic shared library load occurs.

## 3.0 On Windows

    #include <windows.h>
    #include <process.h>

    unsigned _stdcall startlisp(void *x)
    {
      StartLisp((char *) x, 1, 1);
    }

    int
    WINAPI
    WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,
            int nCmdShow)
    {
      HANDLE handle;
      int lisp_thread_id;

    /* custom code can go here */
      MessageBox(NULL, "custom code could run now", "before Lisp starts",
                 MB_OK);

      handle = (HANDLE) _beginthreadex(0, 0, startlisp, (void *) lpCmdLine, 0,
                                       &lisp_thread_id);

      WaitForSingleObject(handle, INFINITE);

    /* custom code can go here; it runs after Lisp exits */
      MessageBox(NULL, "custom code could run now", "after Lisp exits",
                 MB_OK);

      return 0;
    }

## 3.1 Remarks on main() on Windows

1. The `StartLisp()` function is in elm[version].dll (the name of this file will vary, so check your Allegro CL directory for the correct name of this .dll and the elm[version].lib). Assuming the above code is in a file named testm.c, an executable named testm.exe is produced by the command:
   cl testm.c /Zi /MD /link elm[version].lib user32.lib
2. There is no default Lisp image filename - either the user specifies one using the -I command line option, or a file dialog will pop up.
3. If the loaded Lisp image includes Common Graphics (and it does if it includes the Integrated Development Environment), it must be invoked like this:

      testm -I allegro.dxl -e '(load "gdi32.dll")'
            -e '(load "user32.dll")'
            -e (load  "kernel32.dll")'
            -e '(load "comctl32.dll")'

## Appendix A: Potential problems with user-defined main() on Linux

You may have problems building a custom executable (with a user-defined main()) on a version of Redhat later than the one on which your version of Allegro CL was built. If you run into this problem, please contact Franz Inc. Technical Support (send email to support@franz.com).

Copyright (c) 1998-2022, Franz Inc. Lafayette, CA., USA. All rights reserved.
This page was not revised from the 10.0 page.
Created 2019.8.20.

ToC | DocOverview | CGDoc | RelNotes | FAQ | Index | PermutedIndex
| Allegro CL version 10.1 Unrevised from 10.0 to 10.1. 10.0 version