Early anticipation of future development

Asked by jhoechtl

First I would like to express my compliment to what has been achieved until now in so little time. Native compilation, speed, ... JUST great!

I started using Posix-like OSes as of 1997 when I installed my first Debian Linux. I use various Linux Distros until now at home. I am fluent in the Unix way.
Both I developed on and use Windows at work.

* As such I respectfully please you to consider a native port of Ikarus to Windows. cygwin is a no-go and i ever hated it. It's a performance hog had has its glitches and issues all over the way. Windows has it's own way of getting things done and the infrastructure got considerably better during the last years.

* Consider anticipating a threading model, go to a native one. As there already is native code (and as such architecture specific implemetation) don't follow the beaten track of so called "user space threads" and call/cc is not a way to handle web server needs.

Keep on doing the tremendous good job. Looks like Ikarus is looking towards beeing one of the best freeware Scheme implementations!

Kind regards,

Johann

Question information

Language:
English Edit question
Status:
Solved
For:
Ikarus Scheme Edit question
Assignee:
No assignee Edit question
Solved by:
jhoechtl
Solved:
Last query:
Last reply:
Revision history for this message
Abdulaziz Ghuloum (aghuloum) said :
#1

Thank you for your questions and I'm glad you're liking Ikarus so
far. Now for the answers.

> * As such I respectfully please you to consider a native port of
> Ikarus to Windows. cygwin is a no-go and i ever hated it. It's a
> performance hog had has its glitches and issues all over the way.
> Windows has it's own way of getting things done and the
> infrastructure got considerably better during the last years.

The port to Cygwin was done to fulfill one important purpose for the
initial release: to avoid people dismissing Ikarus on the grounds
that it would not run under Windows. Cygwin was the easiest path to
achieve this goal. (Trivia: The Cygwin port took less effort than
writing the autoconf/automake files!)

Now that we're moving forward, we can do the proper port, and *You*
are going to help! :-)

For someone who is familiar with Windows development (I'm not), the
task would take maybe an hour. If accomplished, the native Windows
build may be done by the time for next release. Here it is:

The number of POSIX-specific procedures that Ikarus uses is very
tiny. Maybe Ikarus uses 4 or 5 functions in its C code that would
need porting (mmap/munmap, gettimeofday, getrusage, that's all I can
remember). That's it. Now if you can identify such functions and
point me to the Windows API that would accomplish the same task (I've
heard VirtualAlloc is kind of mmap in Windows), then you would've
saved me huge effort and I would be much indebted to you. Again, you
don't need to provide any code--just the names of the functions and
pointers to documentations would be great. I hope you can help,
though you are under no obligation of course.

> * Consider anticipating a threading model, go to a native one. As
> there already is native code (and as such architecture specific
> implemetation) don't follow the beaten track of so called "user
> space threads" and call/cc is not a way to handle web server needs.

I don't see the two as alternatives but rather as complements.
Ikarus's internals are designed to accommodate both "green" threads
and native threads but actually sitting down and writing and
debugging the threading code is no small feast. Currently, there are
more important R6RS features that are missing (such as the IO layer,
which incidentally was a throw-away hack and will have to be
rewritten now). There is also finishing grad school which is also
overdue now. :-) Native threads will come when the time is right.

> Keep on doing the tremendous good job. Looks like Ikarus is looking
> towards beeing one of the best freeware Scheme implementations!

It's Free as in Freedom.
Thanks.

Aziz,,,

Revision history for this message
jhoechtl (johann-hoechtl) said :
#2

I put together a rather unformated collection of how to solve the mentioned calls on Windows.

The information gathered is from WINE, Postgresql and Squid, sources inspring condfidence.

The Information is 240 lines of assorted C-Code. Should I post here?

Johann

Revision history for this message
Abdulaziz Ghuloum (aghuloum) said :
#3

Sure. Thanks.

Revision history for this message
jhoechtl (johann-hoechtl) said :
#4

--------------------------------------------------------------
getrusage implementation from squid
--------------------------------------------------------------

#if HAVE_WIN32_PSAPI && (defined(_SQUID_MSWIN_) || defined(_SQUID_CYGWIN_))
    if ((WIN32_OS_version == _WIN_OS_WINNT) || (WIN32_OS_version == _WIN_OS_WIN2K)
 || (WIN32_OS_version == _WIN_OS_WINXP))
    {
     /* On Windows NT/2000 call PSAPI.DLL for process Memory */
     /* informations -- Guido Serassio */
     HANDLE hProcess;
     PROCESS_MEMORY_COUNTERS pmc;
 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
     PROCESS_VM_READ,
     FALSE, GetCurrentProcessId());
#if defined (_SQUID_MSWIN_)
 {
 /* Microsoft Visual C++ doesn't have getrusage function, */
 /* so we get process CPU time information from PSAPI.DLL. */
     FILETIME ftCreate, ftExit, ftKernel, ftUser;
     if (GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser))
     {
      LONGLONG tUser64 = *(LONGLONG *)&ftUser;
  LONGLONG tKernel64 = *(LONGLONG *)&ftKernel;
  r->ru_utime.tv_usec =(DWORD)(tUser64 / 10);
  r->ru_stime.tv_usec =(DWORD)(tKernel64 / 10);
     }
 }
#endif
 if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) )
 {
     r->ru_maxrss=(DWORD)(pmc.WorkingSetSize /1024);
     r->ru_majflt=pmc.PageFaultCount;
 }
 CloseHandle( hProcess );
    }
#endif

--------------------------------------------------------------
getrusage implementation from postgresql
_dosmaperr is missing, but discussion about the sanity of this
function is ongoing anyway
--------------------------------------------------------------

     FILETIME starttime;
     FILETIME exittime;
     FILETIME kerneltime;
     FILETIME usertime;
     ULARGE_INTEGER li;

     if (rusage == (struct rusage *) NULL)
     {
         errno = EFAULT;
         return -1;
     }
     memset(rusage, 0, sizeof(struct rusage));
     if (GetProcessTimes(GetCurrentProcess(),
                         &starttime, &exittime, &kerneltime, &usertime) == 0)
     {
         _dosmaperr(GetLastError());
         return -1;
     }

     /* Convert FILETIMEs (0.1 us) to struct timeval */
     memcpy(&li, &kerneltime, sizeof(FILETIME));
     li.QuadPart /= 10L; /* Convert to microseconds */
     rusage->ru_stime.tv_sec = li.QuadPart / 1000000L;
     rusage->ru_stime.tv_usec = li.QuadPart % 1000000L;

     memcpy(&li, &usertime, sizeof(FILETIME));
     li.QuadPart /= 10L; /* Convert to microseconds */
     rusage->ru_utime.tv_sec = li.QuadPart / 1000000L;
     rusage->ru_utime.tv_usec = li.QuadPart % 1000000L;

--------------------------------------------------------------
gettimeofday from WINE
--------------------------------------------------------------

#include "config.h"
#include "wine/port.h"

#ifndef HAVE_GETTIMEOFDAY
__inline int gettimeofday(struct timeval *tv, struct timezone *tz)
#ifdef _WINDOWS
{
    FILETIME ft;
    LARGE_INTEGER li;
    __int64 t;
    static int tzflag;

    if (tv)
    {
        GetSystemTimeAsFileTime(&ft);
        li.LowPart = ft.dwLowDateTime;
        li.HighPart = ft.dwHighDateTime;
        t = li.QuadPart; /* In 100-nanosecond intervals */
        t -= EPOCHFILETIME; /* Offset to the Epoch time */
        t /= 10; /* In microseconds */
        tv->tv_sec = (long)(t / 1000000);
        tv->tv_usec = (long)(t % 1000000);
    }

    if (tz)
    {
        if (!tzflag)
        {
            _tzset();
            tzflag++;
        }
        tz->tz_minuteswest = _timezone / 60;
        tz->tz_dsttime = _daylight;
    }

    return 0;

#else /* !defined(_WINDOWS) */
    errno = ENOSYS;
    return -1;
#endif /* _WINDOWS */
#endif /* HAVE_GETTIMEOFDAY */
}

--------------------------------------------------------------
gettimeofday from OpenAstrah
--------------------------------------------------------------

    #include < time.h >

    #if defined(_MSC_VER) || defined(_MSC_EXTENSIONS)
      #define DELTA_EPOCH_IN_MICROSECS 11644473600000000Ui64
    #else
      #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL
    #endif

    struct timezone
    {
      int tz_minuteswest; /* minutes W of Greenwich */
      int tz_dsttime; /* type of dst correction */
    };

    int gettimeofday(struct timeval *tv, struct timezone *tz)
    {
      FILETIME ft;
      unsigned __int64 tmpres = 0;
      static int tzflag;

      if (NULL != tv)
      {
        GetSystemTimeAsFileTime(&ft);

        tmpres |= ft.dwHighDateTime;
        tmpres <<= 32;
        tmpres |= ft.dwLowDateTime;

        /*converting file time to unix epoch*/
        tmpres -= DELTA_EPOCH_IN_MICROSECS;
        tmpres /= 10; /*convert into microseconds*/
        tv->tv_sec = (long)(tmpres / 1000000UL);
        tv->tv_usec = (long)(tmpres % 1000000UL);
      }

      if (NULL != tz)
      {
        if (!tzflag)
        {
          _tzset();
          tzflag++;
        }
        tz->tz_minuteswest = _timezone / 60;
        tz->tz_dsttime = _daylight;
      }

      return 0;
    }

--------------------------------------------------------------
From http://www.genesys-e.de/jwalter/mix4win.htm (thread safe
implementation) If you feel like omitting thread safety by now,
remove the spin lock calls
--------------------------------------------------------------

/* Wait for spin lock */
int slwait (int *sl) {
    while (InterlockedCompareExchange ((void **) sl, (void *) 1, (void *) 0) != 0)
 Sleep (0);
    return 0;
}
/* Release spin lock */
int slrelease (int *sl) {
    InterlockedExchange (sl, 0);
    return 0;
}

// Global Spinnlock
static int g_sl;

/* getpagesize for windows */
long getpagesize (void) {
    static long g_pagesize = 0;
    if (! g_pagesize) {
        SYSTEM_INFO system_info;
        GetSystemInfo (&system_info);
        g_pagesize = system_info.dwPageSize;
    }
    return g_pagesize;
}
long getregionsize (void) {
    static long g_regionsize = 0;
    if (! g_regionsize) {
        SYSTEM_INFO system_info;
        GetSystemInfo (&system_info);
        g_regionsize = system_info.dwAllocationGranularity;
    }
    return g_regionsize;
}

 /* mmap for windows */
void *mmap (void *ptr, long size, long prot, long type, long handle, long arg) {
    static long g_pagesize;
    static long g_regionsize;
    /* Wait for spin lock */
    slwait (&g_sl);
    /* First time initialization */
    if (! g_pagesize)
        g_pagesize = getpagesize ();
    if (! g_regionsize)
        g_regionsize = getregionsize ();
    /* Allocate this */
    ptr = VirtualAlloc (ptr, size,
   MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN, PAGE_READWRITE);
    if (! ptr) {
        ptr = MMAP_FAILURE;
        goto mmap_exit;
    }
mmap_exit:
    /* Release spin lock */
    slrelease (&g_sl);
    return ptr;
}
/* munmap for windows */
long munmap (void *ptr, long size) {
    static long g_pagesize;
    static long g_regionsize;
    int rc = MUNMAP_FAILURE;
    /* Wait for spin lock */
    slwait (&g_sl);
    /* First time initialization */
    if (! g_pagesize)
        g_pagesize = getpagesize ();
    if (! g_regionsize)
        g_regionsize = getregionsize ();
    /* Free this */
    if (! VirtualFree (ptr, 0,
                       MEM_RELEASE))
        goto munmap_exit;
    rc = 0;
munmap_exit:
    /* Release spin lock */
    slrelease (&g_sl);
    return rc;
}

Revision history for this message
Abdulaziz Ghuloum (aghuloum) said :
#5

Cool. I think this covers it. I'll work on this once I get the IO
reengineered (like after the weekend).
Thanks.

Revision history for this message
leppie (leppie) said :
#6

When trying to trying to compile on Visual C++ on 0.0.1, I noted a lot of non-standard C usage, well just one, but repeated. The Visual C++'s C mode is strict in the sense that all variable need to be declared before any expressions (almost like internal definitions), and hence screams at you, not liking this non-standard C. C++ allows this, and I assume GCC forgives you for it too.

Cheers

Revision history for this message
Abdulaziz Ghuloum (aghuloum) said :
#7

Isn't there any setting in MSVC++ to turn these warnings off? It
would be very painful and error-prone to transform all of these just
to make MSVC++ happy. What about gcc that comes with MinGW? That
doesn't require the cygwin runtime and does generate stand-alone
native windows executables. Did you try it?

Revision history for this message
Michael D. Adams (mdmkolbe) said :
#8

IIRC, C89 does not allow expressions to be mixed with definitions, but C99 relaxes this. Since the code is currently compiled in C89 mode, I'm surprised that it wasn't flagged. leppie, could you give a few line numbers where this is happening in the lastest Bazaar snapshot (or whatever you can get your hands on, new is better)? I want to figure out why GCC didn't flag them.

Revision history for this message
Abdulaziz Ghuloum (aghuloum) said :
#9

It's all over the place. First thing I looked was in the first function in ikarus-collect.c. Hunting and rewriting all of those is a real no-no for me, honestly.

static ikp
meta_alloc_extending(int size, int old_gen, gc_t* gc, int meta_id){
  int mapsize = align_to_next_page(size);
  if(mapsize < extension_amount[meta_id]){
    mapsize = extension_amount[meta_id];
  }
  meta_t* meta = &gc->meta[old_gen][meta_id];
  if((meta_id != meta_data) && meta->base){
    qupages_t* p = ik_malloc(sizeof(qupages_t));
    ikp aq = meta->aq;
    ikp ap = meta->ap;
    ikp ep = meta->ep;
    p->p = aq;
    p->q = ap;
    p->next = gc->queues[meta_id];
    gc->queues[meta_id] = p;
    ikp x = ap;
    while(x < ep){
      ref(x, 0) = 0;
      x += wordsize;
    }
  }
  ikp mem = ik_mmap_typed(
      mapsize,
      meta_mt[meta_id] | next_gen_tag[old_gen],
      gc->pcb);
  gc->segment_vector = gc->pcb->segment_vector;
  meta->ap = mem + size;
  meta->aq = mem;
  meta->ep = mem + mapsize;
  meta->base = mem;
  return mem;
}

Revision history for this message
Abdulaziz Ghuloum (aghuloum) said :
#10

GCC compiles in gnu89 mode which is none of C89, C90, or C99. :-)

           gnu89
               Default, ISO C90 plus GNU extensions (including some C99 fea-
               tures).

Revision history for this message
Michael D. Adams (mdmkolbe) said :
#11

IIRC, moving to C99 actually broke a lot of things (I don't remember if it was warnings, error or runtime bugs), so it looks like things will have to be left as they are for now.

Revision history for this message
leppie (leppie) said :
#12

Yes they are all over the place :(

The ugly hack is to just 'brace' them, eg:

void foo() {
  int a = 0;
  a++;
  a = a + 1;
  { // introduces a new 'block'
    int b = 0;
    b = a + b;
  }
}

Like I said, ugly, but it works :)

Revision history for this message
leppie (leppie) said :
#13

I just tested the files compiling them as C++ instead, it seems the compiler has a much less harder time, and swallows the syntax, and chokes finally on missing deps/type issues/etc.

You can force this with /TP (/TC for C). It defaults on the file extension.

Revision history for this message
redoz (redoz) said :
#14

Is there any news on a native win32 version? My C is very rusty but I'll try to help out if I can.