---
# System prepended metadata

title: Resposta da prova de concorrente

---

# Resposta da prova de concorrente

Ideia do enunciado da prova, não lembro exatamente o que era pra fazer: Implementar duas funções `exec_load` e `exec`. A função `exec_load` recebe uma lista de requisições e cada requisiçao tem uma requisição "pai", apenas uma, menos a requisição 0, que não tem pai. 

A função exec implementa a chamada a uma função que fará a requisição, porém se a requisiçao demorar mais que 5 segundos ele tem q dar timeout e se a requisição der timeout ela tem que retornar -1, caso contrário deve retornar 0.

Além disso, a função `exec_load` recebe um parametro que é o *max_workers*, sendo esse o número máximo de requisições que podem estar executando ao mesmo tempo.

Por fim, a principal restrição do problema é: Uma requisição só pode ser executada depois que os seus antepaçados são executados.

## ìndice

- [Tipo Request](#tipo-request)
- [Definindo o tipo Node](#Definindo-tipo-novo)
- [Executando de fato a requisicao](#Executando-de-fato-a-requisicao)
- [Relacionando o node a execucao](#Relacionando-o-node-a-execucao)
- [A função exec_load](#A-funcao-exec_load)
- [Código completo](#Codigo-completo)

## Tipo request

```go=
type request struct {
    req_id int
    antpassado_id int
    succs []int
}
```

Tipo request que foi dado na questão para manipularmos

## Definindo tipo novo
```go=
type node struct {
    req_id int
    succs []Node // Os filhos do nó atual
}
```
Tipo nó que possui os atributos:

- `req_id` *(int)*: Id da requisição que esse nó deve fazer;
- `succs` *(Node list)*: Lista de filhos (do tipo *Node* também) que esse nó possui;


## Executando de fato a requisicao

A função a seguir serve para fazer de fato a requisição, essa é uma função que foi exigida que nós implementassemos.

```go=
func exec(r request) int {
    ticker := time.Ticker(5 * time.Second)
    reqChan := make(chan int, 1)
    go do_exec(r, reqchan)
    
    select {
        case <- ticker:
            return -1
        case <- reqChan:
            return 0
    }
}
```

Na função acima temos:

- **ticker**: Em Go o `time.ticker` retorna um canal que a cada 5 segundos, nesse caso, adiciona no canal um valor aleatorio padrao do tipo, 0 nesse caso, acho);
- **do_exec**: Função abstraída que vai servir para fazer de fato a requisição, ela recebe o objeto requisição passado como parâmetro da função e o canal cujo o valor de retorno da requisição vai ser adicionado;
- **reqChan**: Canal que vai servir para colocarmos o resultado da requisição.

Agora vamos para o **select**. O select serve pra a gente dizer pra os canais que estao nas clausulas `case` assim: "Olhe vcs dois, quero o conteúdo que tem no canal de vcs, mas vou pegar o primeiro que chegar!", dessa forma, se a requisição demorar mais do que 5 segundos a função retorna -1, caso contrário retona 0.

## Relacionando o node a execucao

```go=
func execReq(node Node, bufferReq chan int) {
    req := request(node.req_id)
    bufferReq <- req_id
    result := exec(req)
    <- bufferReq
    
    for soc in node.soccs {
        execReq(soc, bufferReq)
    }
}
```

Na função acima temos:

- **request()**: Função que não precisa implementar que recebe o id da requisição do nó e retorna a requisição de fato;

- **bufferReq**: Canal que servirá para limitarmos a quantidade máxima de requisições que são executadas de forma paralela;

As linhas 3-5 da função acima tem como objetivo evitar que um número de requisições que seja maior do que o tamanho do buffer sejam executadas ao mesmo tempo, dessa forma, antes de executar a função [exec](#Executando-de-fato-a-requisicao) é adicionado no buffer um número arbitrario e depois de executada a função é retirado um valor do buffer, dando lugar a outra gouroutine qu esteja bloqueada esperando uma vaga para executar sua requisição.

Nas linahs 7 e 8 cada filho do nó atual roda a função `execReq`, sendo essa uma função recursiva que vai ser retornada quando o chegarmos nos nós folha.

## A funcao exec_load

```go=
func exec_load(workload []request, max_workers int) {
    bufferRequest := make(chan int, max_workers)
    rootNode := createTree(workload)
    execReq(rootNode, bufferRequest)
}
```

Na função acima temos:

- **bufferRequest** *(chan int)*: Canal buffer que servirá para controlar a quantidade de requisições que podem ser feitas de forma paralela;
- **createTree** *(func)*: Função arbitraria que não precisou ser implementada, mas que transforma a lista de requests em uma árvore e retorna o nó raiz.

Essa função precisava ser implementada e é a que inicia todo o processo de chamadas das requisições, a partir do nó raiz.

## Codigo completo

```go=
type node struct {
    req_id int
    succs []Node // Os filhos do nó atual
}

func exec(r request) int {
    ticker := time.Ticker(5 * time.Second)
    reqChan := make(chan int, 1)
    go do_exec(r, reqchan)
    
    select {
        case <- ticker:
            return -1
        case <- reqChan:
            return 0
    }
}

func execReq(node Node, bufferReq chan int) {
    req := request(node.req_id)
    bufferReq <- req_id
    result := exec(req)
    <- bufferReq
    
    for soc in node.soccs {
        execReq(soc, bufferReq)
    }
}

func exec_load(workload []request, max_workers int) {
    bufferRequest := make(chan int, max_workers)
    rootNode := createTree(workload)
    execReq(rootNode, bufferRequest)
}
```