Uhc Library Documentation

Ehc

1 How to add a new package

Currently you will need to modify src/ehc/variant.mk and add it to the entry list EHC_PACKAGES_ASSUMED. The order matters since it will be the the order of compilation.

If the package makes use of c headers then you'll need to also modify ehclib/files2.mk. When doing so lookup for the following entries (you can easily follow the example of the other packages) :

  • EHCLIB_SYNC_ALL_PKG_name* - if the package has files that need to be copy from the archive;
  • EHCLIB_ASIS_ALL_SRC_name_ASIS - if the package has c headers files;
  • EHCLIB_ASIS_ALL_DRV_name_ASIS - if the package has c headers files;
  • EHCLIB_FROZEN_ALL_DRV_name_ASIS - if the package has haskell file which are copied from archive;
  • bellow the line # plainly copy .h files to install directly] - you need to add an entry if the package makes use of c headers.
Here name* is the name of the package and * means that there are multiple entries which you'll need to modify.

If the package needs to be configure (eg. has files like HsBaseConfig.h.in) unfortunately you'll need to modify configure.ac file and generate the configure again with autoconf. The entry you'll need to adapt is AC_CONFIG_HEADERS.

2 Known issues

There are several issues in the current implementation of libraries. Some with side effects...

  • GC crash. The bug is caused by the call to print inside the weak ptr finalizer (eg:
    mkWeak x x (Just (print "fin"))). The crash occurs at the level of withHandle_' function when trying to take the Handle__ from the MVar (Handle.chs:175).
  • Nested withHandle calls. If calls to withHandle are nested in the current implementation then a runtime error will occur "UHC.MVar.takeMVar: MVar holds nothing". This is because the first withHandle will take the MVar leaving it empty. When the (nested) second call will try to take the same MVar (now empty) the program will crash since UHC does not block but fail (currently concurency is not supported). An workaround can be inspected in the implementation of hGetContents.
  • Segmentation fault. Although this is not an issue specific to the library I mention it here since it may happen when regress testing the library. Occasionally, I would say non-deterministic, running a test case will cause a segmentation fault error to occur. Workaround: re-run the test.

3 Notes, suggestions

This notes are here as suggestions for further improvements.

  • In order to configure a package this must modify autoconf.ac and generate the configure again. This is not very nice and can/will be correcting by building the libraries with cabal. Also, another side effect of this is that the global configure is polluted with tests for flags needed only in certain libraries.
  • configure.ac can be made nicer by not checking the size of the flags and then test with ifdefs in headers but rather output directly the type. I think GHC variant of configure is much better. It defines a macro FP_CHECK_HTYPE which produces the correct C type in the headers file. It doesn't use sizeof but rather define the type direclty.
    For example:
    FP_CHECK_HTYPE(tcflag_t) will produce Word32. Thus putting #undef HTYPE_TCFLAG_T in HsBaseConfig.h.in will produce the same result as:
    AC_CHECK_SIZEOF(tcflag_,,FP_INCLUDES_MORE) and the following in HsBaseConfig.h.in
    #ifdef SIZEOF_TCFLAG_T
    # if SIZEOF_TCFLAG_T == 8
    #  define HTYPE_TCFLAG_T          Word64
    # elif SIZEOF_TCFLAG_T == 4
    #  define HTYPE_TCFLAG_T          Word32
    # elif SIZEOF_TCFLAG_T == 2
    #  define HTYPE_TCFLAG_T          Word16
    # else
    # endif
    #endif
    
  • Working with sources from both the archive and the repository sometimes seems confusing. I think that when dealing with a particular pacckage it's best to work only with repository or only with the archive.

4 Modifications

This is here in as a reminder for some modifications that occurred during the library implementation.

Among the configuration files modified are:

  • ehclib/files2.mk
  • configure.ac - generating a new configure file
  • src/ehc/variant.mk

Some other reminders:

  • The unix package is largely dependent of lots of external stuff (through #ifdefs). I imported only Files and Directory used in standard packages. Thus I added configuration only for these two - most of the HsUnixConfig.h.in is still unconfigured, meaning that #ifdef tests are not added in configure.ac. Proabably it will be best to migrate to some sort of building mechanism as in GHC, where packages are configured individually instead of globally.
  • In order to fix the test for some #ifedef falgs and in the same time to keep the current design of configure.ac I defined a macro, FP_INCLUDES_MORE which defines additional headers and pass it to AC_CHECK_SIZEOF for the broken definitions. This fixed:
    cc_t, speed_t, tcflag_t, rlim_t, clock_t.
  • When an hsc file need some c headers this is not copied at the build location. The hack was to include the original include directory when compile the file with hsc2hs.
  • Because hsc2hs does not work properly in cywin/windows I do its job manually. This means, generating the corresponding c code, compile and run it (all this are already in the makefile files2.mk so no external manual actions are required). The problem was tricky: hsc2hs (and in general ghc) is independent of cygwin, thus it threats paths in windows style, and, forwards them in windows style to subsequent commands (eg. gcc), which in turn don't understand them. The apparent solution (found on mailing lists) is to pass "ghc" as the c compiler, but then ghc does not know about specific cygwin include directories (eg./usr/include) which will cause big problems. The solution is to simulate the work of hsc2hs: generate the c file, compile and run it manually.
  • I discovered an issue with laziness inside the IO monad. I fixed the issue but I document it here since the fix may be considered a hack or it may be the case that it must be applied in other similar cases. Even if the example could be smaller I give it as a whole in order to catch the ugly part.
    The function hPutChar when combined with line buffering will call the following function:
    hPutcBuffered :: Handle__ -> Bool -> Char -> IO ()
    hPutcBuffered handle_ is_line c = do
      let ref = haBuffer handle_
      buf <- readIORef ref
      let w = bufWPtr buf
      {- *** trace "After: " traceBuf buf -}
      w'  <- writeCharIntoBuffer (bufBuf buf) w c
      let new_buf = buf{ bufWPtr = w' }
      {- *** trace "After: " traceBuf new_buf -}
      if bufferFull new_buf || is_line && c == '\n'
         then do
            flushed_buf <- flushWriteBuffer (haFD handle_) (haIsStream handle_) new_buf
            writeIORef ref flushed_buf
         else do
            writeIORef ref new_buf
    

    Now if you look at the commented lines marked with * one would expect that printing the buffer after writeCharIntoBuffer, would yield the new buffer (with one more character). But in fact, because for some reason, the writing seems to be done lazy, the character is not actually written and only the new size is reported. Thus w'=w+1 but the character is not actually written yet. This will lead to an incorrect output when flushing.
    The solution was to modify writeCharIntoBuffer and make it evaluate the "world" before returning the result.