Thread.Sleep e horário do sistema

Olá.

Tenho o seguinte cenário:
Inicio uma thread que de tempos em tempos realiza um processo de sincronização de horário, nesta thread utilizo um .sleep() de 5 segundos para não usar todo o recurso da máquina. A fim de validar este procedimento, alterei o horário da máquina para verificar se a sincronização era realizada. Ao alterar o relógio para o futuro, o processo funciona, porém, ao atrasar o relógio a execução para no Thread.Sleep(5000). Isso só está ocorrendo no SO Linux, no windows não consegui reproduzir este problema, porém preciso que funcione no linux.

Alguém saberia se tem algo que possa resolver este problema? porque o java responde desta forma?

Lembrando que o processo que uso para sincronizar o horário não chega a ser executado, então não tem relação ao problema. Mesmo porque qualquer Thead.Sleep() fica congelado.

Obrigado.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6311057

Me parece que isso já foi resolvido. Ao menos é o que diz na página do bug.

Fiz um teste aqui:

import java.util.Date;

public class Sleep {

        public static void main(String[] args) throws InterruptedException {
                System.out.println("Vou dormir por 10 segundos...");
                System.out.println(new Date());
                Thread.sleep(10000);
                System.out.println("OK!");
                System.out.println(new Date());
        }

}
$ java -version
java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.2) (6b20-1.9.2-0ubuntu1)
OpenJDK Client VM (build 19.0-b09, mixed mode, sharing)
$ uname -a
Linux 2.6.37-020637rc2-generic #201011160905 SMP Tue Nov 16 10:15:47 UTC 2010 i686 GNU/Linux

Ao executar o programa, obtive os resultados:

$ java Sleep 
Vou dormir por 10 segundos...
Wed Dec 08 11:50:15 BRST 2010
OK!
Wed Dec 08 11:50:26 BRST 2010
$ java Sleep 
Vou dormir por 10 segundos...
Wed Dec 08 11:50:38 BRST 2010
OK!
Wed Dec 08 09:50:43 BRST 2010
$ java Sleep 
Vou dormir por 10 segundos...
Wed Dec 08 09:51:01 BRST 2010
OK!
Wed Dec 08 11:51:05 BRST 2010

Funcionou tanto sem alterar a hora, quanto alterando para o futuro ou para o passado (só não foi tão preciso assim o tempo que a Thread dormiu…).

Ainda tem gente que roda Java 5.0, onde esse problema não foi resolvido :frowning:

Nova descoberta. Testei em Ubuntu e o thread.sleep() não parou. Mas no fedora o problema acontece, e tenho a necessidade que funcione nele. Será que o marcobiscaro2112 ou alguém conseguiria testar no fedora ?

Sobre a página do bug , nela está citando CLOCK_MONOTONIC, onde pelo que entendi através dele o calculo de hora não sofre alterações. Pelo java, conforme pesquisei é acessado via System.NanoTime(), porém não consegui vincular essa funcionalidade para Thread.Sleep().

Meu ambiente é:

java -version
java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8.2) (fedora-43.1.8.2.fc13-x86_64)
OpenJDK 64-Bit Server VM (build 14.0-b16, mixed mode)
 uname -a
Linux 2.6.34.7-61.fc13.x86_64 #1 SMP Tue Oct 19 04:06:30 UTC 2010 x86_64 x86_64 x86_64 GNU/Linux

Se alguem tiver alguma idéia, é bem - vinda.

Obrigado.

Puxa, será que tem alguma diferença entre o IcedTea 1.8.2 e o 1.9.2 que incluiu (ou não) essa alteração?

De qualquer maneira, em um dos comentários nesse bug da Sun, indicou-se que talvez você possa trocar por

synchronized(this) { wait(1000); }

[quote=entanglement]Puxa, será que tem alguma diferença entre o IcedTea 1.8.2 e o 1.9.2 que incluiu (ou não) essa alteração?

De qualquer maneira, em um dos comentários nesse bug da Sun, indicou-se que talvez você possa trocar por

synchronized(this) { wait(1000); } [/quote]

Descobri que este problema ocorre no Fedora 64 bits. Testei em um fedora 32 bits e não parou no sleep.

entanglement , também observei esse comentário.
Vou testar e já posto.

Valeu.

Com wait ocorre o mesmo problema.
Talvez o usuário que postou sobre o wait não tenha percebido um detalhe. Se o relógio for atrasado e neste momento a thread não está parada no wait, o problema não ocorre, ou seja , quando ele parar no wait, vai esperar os milissegundos do parametro e sair normal, porém, se o atraso do relógio for gerado no momento em que a execução estiver parada no wait, ai sim o problema ocorre. Após perceber isso também verifiquei no caso do Thread.Sleep() e percebi que essas condições também acontecem para ele.

Agradeço qualquer nova ajuda.

Obrigado.

Puxa - será que tal bug só foi corrigido no Linux 32 bits? É que o Ubuntu do Marco Biscaro é em 32 bits.

(A propósito, não tenho aqui uma máquina com Linux em que eu tenha acesso administrativo para testar. Não consigo lhe afirmar nada - embora seja perfeitamente possível baixar os fontes do OpenJDK para ver se o pessoal da saudosa Sun corrigiu mesmo isso em 64 bits também.)

[quote=entanglement]Puxa - será que tal bug só foi corrigido no Linux 32 bits? É que o Ubuntu do Marco Biscaro é em 32 bits.

(A propósito, não tenho aqui uma máquina com Linux em que eu tenha acesso administrativo para testar. Não consigo lhe afirmar nada - embora seja perfeitamente possível baixar os fontes do OpenJDK para ver se o pessoal da saudosa Sun corrigiu mesmo isso em 64 bits também.)
[/quote]

Entendo.
É , olhar o jdk é uma possibilidade, embora essa resposta não iria ajudar muito pois o fato é que não está funcionando.

Também não tenho uma máquina 64 bits para testar. Mas olhando o código fonte do JDK 6, não houve mudança na implementação do sleep do build 18 para o build 20.

O código fonte do b18 ( http://hg.openjdk.java.net/jdk6/jdk6/hotspot/file/7dbe24cb959c/src/os/linux/vm/os_linux.cpp ), na linha 1364 começa o método que resgata o tempo atual do sistema. Teoricamente, já está corrigido o problema (se suportado, ele usa o relógio monotônico).

Esse método e o método os::sleep (linha 2790) estão idênticos entre o b18 e o b20.

Não encontrei nenhum indício que há distinção de métodos entre uma JVM 32 bits e uma 64 bits.

Será que não procurei direito ou o problema está em outro lugar?

Aliás, no método os::sleep, caso o tempo do sistema seja mudado para o passado, ele simplesmente não altera o tempo que ainda falta para dormir:

for (;;) {
  if (os::is_interrupted(thread, true)) {
    return OS_INTRPT;
  }

  jlong newtime = javaTimeNanos();

  if (newtime - prevtime < 0) {
    // time moving backwards, should only happen if no monotonic clock
    // not a guarantee() because JVM should not abort on kernel/glibc bugs
    assert(!Linux::supports_monotonic_clock(), "time moving backwards");
    // NÃO ALTERA O VALOR DE millis
  } else {
    millis -= (newtime - prevtime) / NANOSECS_PER_MILLISECS;
  }

  if(millis <= 0) { // NO CASO DE VOLTAR O TEMPO, ISSO NÃO OCORRERÁ
    return OS_OK;
  }

  prevtime = newtime;

  {
    assert(thread->is_Java_thread(), "sanity check");
    JavaThread *jt = (JavaThread *) thread;
    ThreadBlockInVM tbivm(jt);
    OSThreadWaitState osts(jt->osthread(), false /* not Object.wait() */);

    jt->set_suspend_equivalent();
    // cleared by handle_special_suspend_equivalent_condition() or
    // java_suspend_self() via check_and_wait_while_suspended()

    slp->park(millis);

    // were we externally suspended while we were waiting?
    jt->check_and_wait_while_suspended();
  }
}

Isso significa que o mesmo problema reportado ainda ocorre caso o kernel não suporte o relógio de hardware (ou caso a JVM, por algum motivo, não reconheça o suporte). Seria esse o caso?

[quote=marcobiscaro2112]Também não tenho uma máquina 64 bits para testar. Mas olhando o código fonte do JDK 6, não houve mudança na implementação do sleep do build 18 para o build 20.

O código fonte do b18 ( http://hg.openjdk.java.net/jdk6/jdk6/hotspot/file/7dbe24cb959c/src/os/linux/vm/os_linux.cpp ), na linha 1364 começa o método que resgata o tempo atual do sistema. Teoricamente, já está corrigido o problema (se suportado, ele usa o relógio monotônico).

Esse método e o método os::sleep (linha 2790) estão idênticos entre o b18 e o b20.

Não encontrei nenhum indício que há distinção de métodos entre uma JVM 32 bits e uma 64 bits.

Será que não procurei direito ou o problema está em outro lugar?[/quote]

Interessante.

Eu também tenho dúvidas com relação aos métodos.
Talvez uma saída fosse verificar se o relógio monotônico está disponível na máquina, alguém sabe como se faz isso ?
Talvez ele não desse mais problema se eu conseguisse ativar isso.

[quote=vctlzac]Talvez uma saída fosse verificar se o relógio monotônico está disponível na máquina, alguém sabe como se faz isso ?
Talvez ele não desse mais problema se eu conseguisse ativar isso.[/quote]
Bem, extraindo a parte que verifica o suporte ao relógio via hardware, chegamos a isso:

# include <stdio.h>
# include <dlfcn.h>
# include <time.h>

#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC (1)
#endif

int main() {
  void* handle = dlopen("librt.so.1", RTLD_LAZY);
  if (handle == NULL) {
    handle = dlopen("librt.so", RTLD_LAZY);
  }

  if (handle) {
    int (*clock_getres_func)(clockid_t, struct timespec*) =
           (int(*)(clockid_t, struct timespec*))dlsym(handle, "clock_getres");
    int (*clock_gettime_func)(clockid_t, struct timespec*) =
           (int(*)(clockid_t, struct timespec*))dlsym(handle, "clock_gettime");
    if (clock_getres_func && clock_gettime_func) {
      struct timespec res;
      struct timespec tp;
      if (clock_getres_func (CLOCK_MONOTONIC, &res) == 0 &&
          clock_gettime_func(CLOCK_MONOTONIC, &tp)  == 0) {
        printf("OK!\n");
      } else {
        printf("Sem suporte...\n");
      }
    }
    dlclose(handle);
  }
}

Você pode compilar e executar isso nas duas máquinas (lembrando que é preciso compilar uma vez em cada máquina - 32 e 64 bits):

g++ teste.cpp -ldl -o teste
./teste

Compile o seguinte código:

#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <string.h>

int
main ()
{
    printf ("%d\n", sysconf(_SC_MONOTONIC_CLOCK));

}

Se NÃO imprimir um valor de 200112 então você tem um problema.

EDIT- Eu acabei escrevendo a palavra errada, sorry. É se NÂO imprimir.

[quote=entanglement]Compile o seguinte código:

#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
#include <string.h>

int
main ()
{
    printf ("%d\n", sysconf(_SC_MONOTONIC_CLOCK));

}

Se NÃO imprimir um valor de 200112 então você tem um problema.

EDIT- Eu acabei escrevendo a palavra errada, sorry. É se NÂO imprimir. [/quote]

Ao executar este último código, o retorno foi “200809”.

Sabe dizer o que representa ?

[quote=vctlzac]Ao executar este último código, o retorno foi “200809”.

Sabe dizer o que representa ?[/quote]
O mesmo aqui. Você testou no Fedora 64 bits?

[quote=marcobiscaro2112][quote=vctlzac]Ao executar este último código, o retorno foi “200809”.

Sabe dizer o que representa ?[/quote]
O mesmo aqui. Você testou no Fedora 64 bits?[/quote]

Sim , este valor 200809 foi gerado no fedora 64 bits.

Só pra eu entender, valores diferentes de 200112 representam problema ?

[quote=vctlzac][quote=marcobiscaro2112][quote=vctlzac]Ao executar este último código, o retorno foi “200809”.

Sabe dizer o que representa ?[/quote]
O mesmo aqui. Você testou no Fedora 64 bits?[/quote]

Sim , este valor 200809 foi gerado no fedora 64 bits.

Só pra eu entender, valores diferentes de 200112 representam problema ?[/quote]
Se representarem, estou com um problema. Mas quando voltei o relógio do sistema a Thread retomou de forma correta. Você rodou o código que postei?

[quote=marcobiscaro2112][quote=vctlzac][quote=marcobiscaro2112][quote=vctlzac]Ao executar este último código, o retorno foi “200809”.

Sabe dizer o que representa ?[/quote]
O mesmo aqui. Você testou no Fedora 64 bits?[/quote]

Sim , este valor 200809 foi gerado no fedora 64 bits.

Só pra eu entender, valores diferentes de 200112 representam problema ?[/quote]
Se representarem, estou com um problema. Mas quando voltei o relógio do sistema a Thread retomou de forma correta. Você rodou o código que postei?[/quote]

Você diz este código ?

# include <stdio.h>
# include <dlfcn.h>
# include <time.h>

#ifndef CLOCK_MONOTONIC
#define CLOCK_MONOTONIC (1)
#endif

int main() {
  void* handle = dlopen("librt.so.1", RTLD_LAZY);
  if (handle == NULL) {
    handle = dlopen("librt.so", RTLD_LAZY);
  }

  if (handle) {
    int (*clock_getres_func)(clockid_t, struct timespec*) =
           (int(*)(clockid_t, struct timespec*))dlsym(handle, "clock_getres");
    int (*clock_gettime_func)(clockid_t, struct timespec*) =
           (int(*)(clockid_t, struct timespec*))dlsym(handle, "clock_gettime");
    if (clock_getres_func && clock_gettime_func) {
      struct timespec res;
      struct timespec tp;
      if (clock_getres_func (CLOCK_MONOTONIC, &res) == 0 &&
          clock_gettime_func(CLOCK_MONOTONIC, &tp)  == 0) {
        printf("OK!\n");
      } else {
        printf("Sem suporte...\n");
      }
    }
    dlclose(handle);
  }
}

Este não executei … achei que se rodasse o outro poderia descartar este.
O que esse código faz ?