• Nenhum resultado encontrado

O primeiro parâmetro a ser considerado na criação de uma nova simulação é o tipo (ou escala) de tempo que será adotado. Tal seleção está diretamente ligada à natureza da simulação. Se os objetos da simulação executam seus comandos na escala de microssegundos e executam seus comandos durante pouco tempo de simulação, seria conveniente considerar a unidade numérica definida na classe BaSSTime como microssegundo ou segundo. A formatação do número poderia incluir a unidade (µs ou s) para efeitos de exibição do valor temporal, sendo isso conseguido através da implementação do método toString() em uma subclasse de BaSSTime. Uma simulação geológica, por outro lado, necessitaria de escalas de tempo da ordem de milhões/bilhões de anos, sendo conveniente criar uma subclasse de BaSSTime para esse propósito.

A classe BaSSStandardTime (subclasse de BaSSTime) representa o tempo da forma mais usual, isto é, na escala dos eventos em que o homem está acostumado a vivenciar no dia-a-dia. O tempo pode ser determinado em anos, meses, dias, horas, segundos e milissegundos. Tanto o método construtor como o método toString() consideram o tempo no formato YYyMMmDDdHH:mm:ss.SSS, onde YY refere-se a anos, MM minutos, DD dias, HH

horas, mm minutos, ss segundos e SSS milésimos de segundos. A chamada do construtor BaSSStandardTime( "11d03:25:32.264" ), por exemplo, cria um objeto com valor temporal de 11 dias, 3 horas, 25 minutos e 32,264 segundos. Deve-se ressaltar que o valor armazenado internamente ao objeto BaSSTime (superclasse) é sempre um número representado no padrão IEEE 754, conforme já discutido no item 4.6.1, independente da forma com que o tempo é inserido nas subclasses.

O controle da simulação é realizado pela classe BaSSControl. Para se criar um objeto dessa classe deve-se fornecer como parâmetro a classe que será utilizada como referência de formatação e escala do tempo de simulação. Se, por exemplo, a classe BASSStandardTime for utilizada, a criação de um novo objeto se daria pelo comando new BaSSControl( BaSSStandardTime.class ).

Com relação ao controle do avanço temporal na simulação e a determinação de qual BaSSThread deve ser desbloqueada pelo BaSSControl, são utilizados os atributos simulation_time (tempo de simulação) do objeto da classe BaSSControl, next_simulation_time (próximo tempo de simulação) e priority (prioridade) dos objetos instanciados da classe BaSSThread (vide Figura 4.21). A cada desbloqueio do BaSSControl, há uma busca da BaSSThread que possua o menor next_simulation_time e a maior priority, sendo esta a próxima

Capítulo 4 BaSS – Um framework para o desenvolvimento de simuladores thread a ser desbloqueada. Após esta determinação, o atributo simulation_time é atualizado com o valor de next_simulation_time da BaSSThread que será desbloqueada, avançando, assim, o tempo de simulação. Portanto, o avanço do tempo de simulação é realizado por saltos, conforme o paradigma de programação event-driven.

A lista contendo os objetos da classe BaSSThread não fica ordenada por tempo de simulação, mas sim por prioridade, uma vez que é mais eficiente (em termos computacionais) buscar a BaSSThread com menor next_simulation_time para desbloqueá-la do que ordenar a lista por next_simulation_time toda vez que uma BaSSThread atualiza esse atributo e é bloqueada. Como a lista de BaSSThread está ordenada de forma decrescente por prioridade, sempre será selecionada a thread que possui a maior prioridade. Contudo, se houver a troca de prioridades das BaSSThreads com frequência, o que depende da aplicação, a performance da simulação pode ser prejudicada, uma vez que a cada alteração de prioridade toda a lista de BaSSThread é reordenada.

O tempo de execução da simulação é calculado a partir do início da mesma. O valor presente no relógio do computador é registrado e, a cada consulta ao wallclock_time, uma subtração é realizada do valor presente no relógio no momento da consulta com o valor registrado no início da simulação. O tempo de execução é um objeto do tipo BaSSStandardTime.

No decorrer de uma simulação, o estado da mesma se modifica de acordo com os eventos envolvidos no processo de simulação, mas também pode ser alterado por ações externas à mesma (intervenção do usuário, por exemplo). A Figura 4.22 ilustra o diagrama de estados de simulação implementados na classe BaSSControl.

Quando um objeto do tipo BaSSControl é instanciado, o estado inicial de simulação é NOT STARTED (não iniciado). A partir do momento que é feita a chamada ao método start() do objeto de controle BaSSControl, a simulação se inicia, desbloqueando e bloqueando as BaSSThreads registradas no controle, passando o estado da simulação para RUNNING (executando). É possível pausar a simulação (parada no procedimento de bloqueio e desbloqueio das BaSSThreads), pelo método pause(), fazendo com que a simulação entre no estado PAUSED (pausado). O método resume() é utilizado para retornar à simulação ao estado RUNNING, depois de uma pausa. O tempo em que o simulador fica pausado não é contabilizado no tempo de simulação.

O método pause() deve ser utilizado com cautela quando chamando a partir de uma BaSSThread. Uma vez que a simulação é pausada de dentro de uma BaSSThread, somente um elemento externo à simulação poderá chamar o método resume(), pois toda a

simul

PAUSED

caso, a simulação termina no estado final STOPPED (parado), indicando que a simulação não chegou ao final, mas foi interrompida por um agente externo, possivelmente o usuá simulador.

método tentaria “destruir” a própria

travamento na execução da simulação ou havendo o levantamento de exceções relacio ao mecanismo de controle das

a simulação de fato terminou, isto é, todos os comandos das não há mais nenhuma ação a ser r

COMPLETED estado

possível interrupção que as faça e

simulação, isto é, todas as BaSSThreads da simulação, ficam bloqueadas.

Figura 4.22

Estando a simulação em execução (estado

PAUSED), é possível finalizar a simulação na “força bruta” chamando o método

caso, a simulação termina no estado final STOPPED (parado), indicando que a simulação não chegou ao final, mas foi interrompida por um agente externo, possivelmente o usuá simulador.

O método

método tentaria “destruir” a própria

travamento na execução da simulação ou havendo o levantamento de exceções relacio ao mecanismo de controle das

Existem outras quatro formas da simulação chegar ao fim. A primeira ocorre quando a simulação de fato terminou, isto é, todos os comandos das

não há mais nenhuma ação a ser r COMPLETED

estado WAITING FOREVER possível interrupção que as faça e

ação, isto é, todas as BaSSThreads da simulação, ficam bloqueadas.

Figura 4.22 – Diagrama de estados de simulação implementados na classe

Estando a simulação em execução (estado

), é possível finalizar a simulação na “força bruta” chamando o método

caso, a simulação termina no estado final STOPPED (parado), indicando que a simulação não chegou ao final, mas foi interrompida por um agente externo, possivelmente o usuá

O método stop()

método tentaria “destruir” a própria

travamento na execução da simulação ou havendo o levantamento de exceções relacio ao mecanismo de controle das

Existem outras quatro formas da simulação chegar ao fim. A primeira ocorre quando a simulação de fato terminou, isto é, todos os comandos das

não há mais nenhuma ação a ser r

COMPLETED (concluída). A segunda situação ocorre quando todas as WAITING FOREVER

possível interrupção que as faça e

ação, isto é, todas as BaSSThreads da simulação, ficam bloqueadas.

Diagrama de estados de simulação implementados na classe

Estando a simulação em execução (estado

), é possível finalizar a simulação na “força bruta” chamando o método

caso, a simulação termina no estado final STOPPED (parado), indicando que a simulação não chegou ao final, mas foi interrompida por um agente externo, possivelmente o usuá

stop() não pode ser chamado a partir de uma BaSSThread, pois este método tentaria “destruir” a própria

travamento na execução da simulação ou havendo o levantamento de exceções relacio ao mecanismo de controle das threads

Existem outras quatro formas da simulação chegar ao fim. A primeira ocorre quando a simulação de fato terminou, isto é, todos os comandos das

não há mais nenhuma ação a ser r

(concluída). A segunda situação ocorre quando todas as

WAITING FOREVER (ver item 4.6.2.2), ou seja, estão ociosas aguardando alguma possível interrupção que as faça e

ação, isto é, todas as BaSSThreads da simulação, ficam bloqueadas.

Diagrama de estados de simulação implementados na classe

Estando a simulação em execução (estado

), é possível finalizar a simulação na “força bruta” chamando o método

caso, a simulação termina no estado final STOPPED (parado), indicando que a simulação não chegou ao final, mas foi interrompida por um agente externo, possivelmente o usuá

não pode ser chamado a partir de uma BaSSThread, pois este método tentaria “destruir” a própria BaSSThread

travamento na execução da simulação ou havendo o levantamento de exceções relacio threads na JVM.

Existem outras quatro formas da simulação chegar ao fim. A primeira ocorre quando a simulação de fato terminou, isto é, todos os comandos das

não há mais nenhuma ação a ser realizada. Neste caso, o estado final da simulação é (concluída). A segunda situação ocorre quando todas as

(ver item 4.6.2.2), ou seja, estão ociosas aguardando alguma possível interrupção que as faça executar alguma ação. Mas se todas as

ação, isto é, todas as BaSSThreads da simulação, ficam bloqueadas.

Diagrama de estados de simulação implementados na classe

Estando a simulação em execução (estado

), é possível finalizar a simulação na “força bruta” chamando o método

caso, a simulação termina no estado final STOPPED (parado), indicando que a simulação não chegou ao final, mas foi interrompida por um agente externo, possivelmente o usuá

não pode ser chamado a partir de uma BaSSThread, pois este BaSSThread que o chamou, podendo provocar um travamento na execução da simulação ou havendo o levantamento de exceções relacio

na JVM.

Existem outras quatro formas da simulação chegar ao fim. A primeira ocorre quando a simulação de fato terminou, isto é, todos os comandos das

ealizada. Neste caso, o estado final da simulação é (concluída). A segunda situação ocorre quando todas as

(ver item 4.6.2.2), ou seja, estão ociosas aguardando alguma xecutar alguma ação. Mas se todas as

ação, isto é, todas as BaSSThreads da simulação, ficam bloqueadas.

Diagrama de estados de simulação implementados na classe

Estando a simulação em execução (estado RUNNING

), é possível finalizar a simulação na “força bruta” chamando o método

caso, a simulação termina no estado final STOPPED (parado), indicando que a simulação não chegou ao final, mas foi interrompida por um agente externo, possivelmente o usuá

não pode ser chamado a partir de uma BaSSThread, pois este que o chamou, podendo provocar um travamento na execução da simulação ou havendo o levantamento de exceções relacio

Existem outras quatro formas da simulação chegar ao fim. A primeira ocorre quando a simulação de fato terminou, isto é, todos os comandos das BaSSThreads

ealizada. Neste caso, o estado final da simulação é (concluída). A segunda situação ocorre quando todas as

(ver item 4.6.2.2), ou seja, estão ociosas aguardando alguma xecutar alguma ação. Mas se todas as

ação, isto é, todas as BaSSThreads da simulação, ficam bloqueadas.

Diagrama de estados de simulação implementados na classe BaSSControl

RUNNING) ou pausada (estado ), é possível finalizar a simulação na “força bruta” chamando o método stop()

caso, a simulação termina no estado final STOPPED (parado), indicando que a simulação não chegou ao final, mas foi interrompida por um agente externo, possivelmente o usuá

não pode ser chamado a partir de uma BaSSThread, pois este que o chamou, podendo provocar um travamento na execução da simulação ou havendo o levantamento de exceções relacio

Existem outras quatro formas da simulação chegar ao fim. A primeira ocorre quando BaSSThreads foram executados e ealizada. Neste caso, o estado final da simulação é (concluída). A segunda situação ocorre quando todas as BaSSThreads

(ver item 4.6.2.2), ou seja, estão ociosas aguardando alguma xecutar alguma ação. Mas se todas as BaSSThreads

BaSSControl.

) ou pausada (estado stop(). Neste caso, a simulação termina no estado final STOPPED (parado), indicando que a simulação não chegou ao final, mas foi interrompida por um agente externo, possivelmente o usuário do não pode ser chamado a partir de uma BaSSThread, pois este que o chamou, podendo provocar um travamento na execução da simulação ou havendo o levantamento de exceções relacionadas Existem outras quatro formas da simulação chegar ao fim. A primeira ocorre quando foram executados e ealizada. Neste caso, o estado final da simulação é BaSSThreads estão no (ver item 4.6.2.2), ou seja, estão ociosas aguardando alguma BaSSThreads estão ) ou pausada (estado . Neste caso, a simulação termina no estado final STOPPED (parado), indicando que a simulação não rio do não pode ser chamado a partir de uma BaSSThread, pois este que o chamou, podendo provocar um nadas Existem outras quatro formas da simulação chegar ao fim. A primeira ocorre quando foram executados e ealizada. Neste caso, o estado final da simulação é estão no (ver item 4.6.2.2), ou seja, estão ociosas aguardando alguma estão

Capítulo 4 BaSS – Um framework para o desenvolvimento de simuladores aguardando indefinidamente, isso significa que nenhuma delas vai gerar uma interrupção para interromper outra, configurando um caso de starvation, levando a simulação ao término pelo estado FINISHED BY WAITING FOREVER (término por esperar para sempre).

O terceiro e quarto casos em que a simulação pode chegar ao fim referem-se a atributos que limitam o tempo de execução ou o tempo de simulação. Se o usuário/programador desejar que a simulação dure um determinado tempo de execução (20 minutos, por exemplo), ele deve atribuir esse tempo limite ao atributo wallclock_time_limit através do método setWallclockTimeLimit() da classe BaSSControl, passando como parâmetro um BaSSStandardTime que limitará o tempo de execução da simulação. Assim, quando o wallclock_time for maior ou igual ao wallclock_time_limit, a simulação termina, levando a simulação para o estado FINISHED BY WALLCLOCK TIME LIMIT (término por limite de tempo de execução). Da mesma forma, se o limitante de tempo não for baseado no tempo de execução da simulação, mas no tempo de simulação, deve-se conferir ao atributo simulation_time_limit o valor limite através do método setSimulationTimeLimit(). Se o simulation_time for maior ou igual ao simulation_time_limit, então a simulação termina e seu estado final será FINISHED BY SIMULATION TIME LIMIT (término por limite de tempo de simulação).

Uma característica particular das threads em Java é que elas não podem ser reiniciadas uma vez que tenham completado. A classe BaSSThread herda essa limitação. Portanto, uma vez que uma simulação tenha chegado ao fim, por qualquer motivo, ela não pode ser reiniciada a não ser o programa de simulação seja executado novamente. Uma estratégia que utiliza a propriedade reflexiva da linguagem Java (Java reflection) pode ser empregada para contornar esse problema. Tal estratégia será abordada no capítulo 5.

A classe BaSSDelay pode ser utilizada para adicionar um atraso no processamento dos comandos que estão em simulação. Este atraso é útil nas situações em que o processamento é demasiadamente rápido e não se consegue acompanhar os eventos em interfaces gráficas, por exemplo. Um BaSSDelay pode ser adicionado à uma BaSSControl através do método setDelay(). Existem dois tipos de atrasos, mas ambos possuem um determinado tempo (wallclock time) estabelecido em milissegundos que é inserido entre cada comando ou conjunto de comandos executados. O primeiro tipo é o EVENT_DELAY, no qual um atraso é inserido toda vez que uma BaSSThread da simulação é bloqueada. O segundo tipo é o SIMULATION_TIME_DELAY, no qual um atraso é criado apenas quando o simulation_time é alterado, ou seja, o tempo de simulação avança.

Quase a totalidade dos simuladores necessita que registro (log) seja criado para processamento dos dados gerados pelas simulações. A interface BaSSLogger permite que

mensagens provindas de todos os objetos pertencentes à simulação sejam registradas. Tal registro é realizado através do método log( BaSSControl control, Object sender, Object... related ) que devem ser implementados nas classes implementadas a partir da interface BaSSLogger. O parâmetro control é o objeto que está realizando o controle da simulação e pode ser utilizado na obtenção do tempo de simulação no qual o registro ocorreu, por exemplo; o parâmetro sender diz respeito ao objeto que está realizando o registro do evento; e o parâmetro related pode ser utilizado para enviar objetos (quantos forem necessários) relacionados ao registro.

Todos objetos da simulação que desejam realizar um registro devem utilizar o método log( Object sender, Object... related ) disponível na classe BaSSControl. Pode haver mais de um BaSSLogger registrado no BaSSControl (registro realizado pelo método addLogger()), sendo que todos eles recebem os registros quando o método log do BaSSControl é invocado.

As implementações da interface BaSSLogger também devem implementar os métodos init() e finalize(). Como os próprios nomes sugerem, esses métodos são chamados no ínicio e no final das simulações, respectivamente.

O BaSSLogger também é útil para registrar as exceções e erros reportados pela linguagem de programação Java. Ao invés de utilizar a saída padrão para exibir as mensagens reportadas pela JVM, pode-se redirecioná-las para este registro interno à simulação.

Além de registros para análise dos dados simulados, foi acrescentado ao modelo do BaSS uma classe de atualizadores, denominada BaSSUpdater. Elas são úteis para se realizar a atualização de interfaces gráficas durante a simulação. Assim como no caso da classe BaSSDelay, a atualização pode ser realizada a cada novo evento processado na simulação

(EVENT_UPDATER) ou a cada avanço do tempo de simulação

(SIMULATION_TIME_UPDATER). O método getUpdaterType() deve ser implementado e retornar um dos dois valores pertencentes à classe de enumeração BaSSUpdater.Type, EVENT_UPDATER ou SIMULATION_TIME_UPDATER.

Seja qual for o tipo de atualização, as ações relacionadas à ela devem ser realizadas no método update( BaSSControl control ) da classe que será desenvolvida para esse fim a partir da interface BaSSUpdater. Os métodos init() e finalize() também devem ser implementados, referindo-se a ações que devem ser realizadas antes do início da simulação e ao término da mesma, respectivamente.

Como no caso dos registros (BaSSLogger), pode-se incluir mais de um BaSSUpdater nas simulações. Para adicioná-los no BaSSControl, deve-se utilizar o método

Capítulo 4 BaSS – Um framework para o desenvolvimento de simuladores addUpdater() e toda a vez que o método update() da classe BaSSControl for chamado, o método update( BaSSControl control ) de cada BaSSUpdater cadastrado no BaSSControl é invocado.

A diferença entre um evento de registro realizado pelo BaSSLogger e uma atualização realizada pelo BaSSUpdater está, portanto, relacionada a quando o método de registro log( Object sender, Object... related ) da classe BaSSLogger é chamado e quando o método de atualização update() da classe BaSSUpdater é chamado. No primeiro caso, quem chama o método de registro são os objetos pertencentes à simulação, ficando a cargo do programador identificar a necessidade de se realizar um registro qualquer. Tal registro pode ser realizado, portanto, por qualquer classe pertencente ao simulador que tenha acesso ao objeto de controle da simulação (objeto da classe BaSSControl). No caso dos atualizadores, quem chama o método de registro é o próprio controle da simulação, sendo o momento de chamada controlado pelo tipo de BaSSUpdater.Type associado ao BaSSUpdater.

Tanto a classe BaSSLogger como a classe BaSSUpdater foram criadas como interfaces para permitir o desenvolvimento de classes que assumam diferentes papeis numa simulação. Uma subclasse de BaSSThread (uma classe que extends BaSSThread), por exemplo, pode ser uma classe que implementa um BaSSLogger (uma classe que implements BaSSLogger), que implementa um BaSSUpdater (uma classe que implements BaSSUpdater) ou ainda que implementa um BaSSLogger e um BaSSUpdater simultaneamente.

Tal flexibilidade, conseguida pelo modo com que as interfaces operam, permite a escrita de simuladores com um menor número de classes. Contudo, o registro dessas diferentes funcionalidades atribuídas a um mesmo objeto deve ser informado ao simulador, isto é, por mais que uma classe seja descrita na linguagem Java como uma BaSSThread, um BaSSLogger e um BaSSUpdater simultaneamente, deve-se adicionar ao controle da simulação (objeto da classe BaSSControl) o mesmo objeto instanciado a partir dessa classe utilizando os métodos addThread( BaSSThread thread ), addLogger( BaSSLogger logger) e addUpdater( BaSSUpdater updater ) para que o objeto em questão assuma os três papeis na simulação. No caso de uma classe implementar simultaneamente as interfaces BaSSLogger e BaSSUpdater, os métodos init( BaSSControl control ) e finalize( BaSSControl control ) serão compartilhados pela implementação dessas interfaces, mas só são executados uma única vez, respectivamente, no início e no fim de cada simulação.

A Figura 4.23 apresenta em pseudocódigo a sequência de eventos realizados pelo BaSSControl no início e no fim de cada simulação, isto é, quando o método start() é chamado para iniciar a simulação e quando a simulação termina (finalize()), independente

do motivo do término (chamada ao método stop(), finalização por tempo limite, simulação concluída ou finalizada por espera infinita).

O BaSS ainda fornece uma classe que gera números pseudoaleatórios, muito útil em simulações nas quais eventos aleatórios devem ocorrer, mas que se faz necessário que esses eventos aleatórios se repitam em diversas execuções da simulação. A classe BaSSRandom pode ser introduzida no BaSSControl pelo método setRandom( BaSSRandom random ) e acessada por qualquer objeto da simulação pelo método getRandom(). O construtor da classe BaSSRandom recebe a semente (um número inteiro) para geração de números pseudoaleatórios, garantindo assim uma repetição controlada dos número gerados a cada simulação realizada. Os métodos nextInt(), nextDouble() e nextBaSSTime(), pertencentes à classe BaSSRandom podem ser utilizados para gerar valore aleatórios para números inteiros, ponto flutuantes do tipo double e valores temporais do tipo BaSSTime,