in reply to Re^3: Standard way to convert timezone in multithreaded script
in thread Standard way to convert timezone in multithreaded script

Looking more closely at your code above, there is something strange going on that I do not understand.

In this part of the main thread, you change the value of $ENV{TZ} twice:

$ENV{TZ}="Europe/Paris"; my $t=localtime(); print "main (Europe/Paris): ",$t,"\n"; $ENV{TZ}="Europe/Moscow"; $t=localtime(); print "main (Europe/Moscow): ",$t,"\n";

But nowhere in that part of the code do you call tzset(), nor any of the functions in your XS code. And yet, according to the console output you've posted:

main (Europe/Paris): Tue Nov 24 23:27:28 2009 main (Europe/Moscow): Wed Nov 25 01:27:28 2009

The output from localtime is somehow affected. Which is exactly what didn't happen originally that caused you to post your OP?


Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
RIP PCW It is as I've been saying!(Audio until 20090817)

Replies are listed 'Best First'.
Re^5: Standard way to convert timezone in multithreaded script
by whale2 (Novice) on Nov 25, 2009 at 09:42 UTC

    XS code contains four calls to tzset()

    Perl code running in main thread works pretty well without calling POSIX::tzset. I believe this is due to how tzset()/localtime() in libc works.

    'man tzset' says "This function is automatically called by the other time conversion functions that depend on the time zone."

    So, there is no wonder why this works in main thread. And in child threads tzset needs to be explicitly called for changing timezone, but really works only when called in XS code. I don't know why, I just managed to find this fact :)

    Also, I did not tried it on other OSes or even on another linux distro or with different libc versions or versions of perl other than 5.10.0

    My environment is CentOS 5.2, libc-2.5, perl-5.10.0

      Oh-uh, my XS code is wrong. Just calling malloc() in threads is dangerous. I'll post corrected code as soon as it really will work :)
      My environment is CentOS 5.2, libc-2.5, perl-5.10.0

      I've been playing with 5.10.1 under Ubuntu 9.10:

      mehere@mehere-desktop:~/test$ gcc --version gcc (Ubuntu 4.4.1-4ubuntu8) 4.4.1 Copyright (C) 2009 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There i +s NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PUR +POSE.

      ...but, as a guest running within a VirtualBox on a Vista host.

      Ie. I have little faith that anything that works (or fails) here will produce the same results under any other circumstances :)

      (P.S.) You aren't the first to be bitten by the "malloc thing" in XS code. Truth beknown--if they'd all admit to it--we've all been there.

      Your discovery regarding not requiring tzset() in the main thread are interesting. It stimulates several possibilities. And when I am less sleep deprived, I may persue them to a (minorly) significant conclusion.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        this XS proved to work:
        #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include "ppport.h" #include <time.h> #include <stdio.h> #include <stdlib.h> #include <string.h> MODULE = tztime PACKAGE = tztime long tz_mktime(sec, min, hour, mday, mon, year, tz_name) int sec int min int hour int mday int mon int year char *tz_name CODE: long epoch; struct tm tz_tm; char *old_tz; const char *tz_p; bool restore = FALSE; tz_tm.tm_sec = sec; tz_tm.tm_min = min; tz_tm.tm_hour = hour; tz_tm.tm_mday = mday; tz_tm.tm_mon = mon; tz_tm.tm_year = year; tz_tm.tm_wday = 0; tz_tm.tm_yday = 0; tz_tm.tm_isdst = -1; tz_p = getenv("TZ"); if(tz_p != NULL) { restore = TRUE; old_tz = malloc(strlen(tz_p) + 1); if(old_tz == NULL) { XSRETURN_UNDEF; } strncpy(old_tz,tz_p,strlen(tz_p) + 1); } setenv("TZ",tz_name,1); tzset(); epoch = (long)mktime(&tz_tm); if(restore) { putenv(old_tz); free(old_tz); } else { unsetenv("TZ"); } tzset(); RETVAL = epoch; OUTPUT: RETVAL void tz_gettime(epoch, tz_name) long epoch char *tz_name; PPCODE: struct tm tz_tm; char *old_tz; const char *tz_p; bool restore = FALSE; tz_p = getenv("TZ"); if(tz_p != NULL) { restore = TRUE; old_tz = malloc(strlen(tz_p) + 1); if(old_tz == NULL) { XPUSHs(sv_2mortal(newSVnv(errno))); } strncpy(old_tz,tz_p,strlen(tz_p) + 1); } setenv("TZ",tz_name,1); tzset(); localtime_r(&epoch,&tz_tm); if(restore) { putenv(old_tz); free(old_tz); } else { unsetenv("TZ"); } tzset(); XPUSHs(sv_2mortal(newSVnv(tz_tm.tm_sec))); XPUSHs(sv_2mortal(newSVnv(tz_tm.tm_min))); XPUSHs(sv_2mortal(newSVnv(tz_tm.tm_hour))); XPUSHs(sv_2mortal(newSVnv(tz_tm.tm_mday))); XPUSHs(sv_2mortal(newSVnv(tz_tm.tm_mon))); XPUSHs(sv_2mortal(newSVnv(tz_tm.tm_year))); XPUSHs(sv_2mortal(newSVnv(tz_tm.tm_wday))); XPUSHs(sv_2mortal(newSVnv(tz_tm.tm_yday))); XPUSHs(sv_2mortal(newSVnv(tz_tm.tm_isdst)));
Re^5: Standard way to convert timezone in multithreaded script
by ikegami (Patriarch) on Nov 25, 2009 at 18:48 UTC

    But nowhere in that part of the code do you call tzset(),

    And adding them doesn't help.