# Haskell concurrent

Haskell concurrent amplia Haskell98 amb concurrència explícita.
Els dos conceptes principals en què es basa Haskell concurrent són les Mutable variables MVar α i la possibilitat d'engegar un nou fil d'execució via forkIO.

## Concurrència
### forkIO

    engega un fil d'execució lleuger del planificador del Run Time System que, segons el compilador de Haskell concurrent, pot implementar multitasca cooperativa o bé arrabassadora (ang:preemptive)).
Vegeu "La implementació de concurrència de GHC".
mecanismes de comunicació :
| tipus | mònada | generadors | mòdul | descripció |
| MVar a | IO | newMVar x -- var buida per a un tipus T newEmptyMVar :: IO (MVar T) | Control.Concurrent.MVar | vars. globals sincronitzades que es buiden en consultar-les doblen la funcionalitat com a * bústia d'un sol element per baldes (ang:locks) * semàfor binari |
| Chan a | IO | newChan x | Control.Concurrent.Chan | bústia amb cua il·limitada, per baldes (ang:locks) |
| TVar a | STM / IO | newTVar x -- encapsulable newTVarIO x -- global | Control.Concurrent.STM.TVar | posicions de memòria compartida suporten transaccions de memòria atòmiques |
| TMVar a | STM / IO | newTMVar x -- encapsulable newTMVarIO x -- global | Control.Concurrent.STM.TMVar | MVar (bústia de comunicació d'un sol elem.) protegida transaccionalment |
| TChan a | STM / IO | newTChan x -- encapsulable newTChanIO x -- global | Control.Concurrent.STM.TChan | bústia de comunicació transaccional amb cua il·limitada |

### accés sincronitzat amb bústies d'un sol element anomenades MVar

En altres llenguatges, per assegurar la correcta actualització d'una variable, cal posar un monitor que impedeix l'accés a altres processos durant la lectura i actualització a la variable sincronitzada.
Aquí la lectura d'una MVar (takeMVar) transfereix l'objecte contingut al fil consumidor, deixant-la buida, provocant que els altres processos que la vulguin llegir hagin d'esperar que el fil productor reposi el contingut actualitzat (amb putMVar), aprofitant així el mecanisme de concurrència per bústies de missatges.
* "takeMVar mvar" bloca el fil d'exec. si mvar estava buida, per tant no disponible
* "putMVar mvar valor" bloca si mvar estava plena, per tant ocupada
Generadors:
Una MVar té tres facetes:
* Variable amb accés sincronitzat
* Bústia de comunicació d'un sol element (takeMVar com a receive, putMVar com a send)
* Semàfor binari (takeMVar com a wait, putMVar com a signal)
Darrerament s'hi han afegit crides no blocants (tryTakeMVar, tryPutMVar).
També tenim modifyMVar_ (composició de takeMVar i putMVar que retorna IO) i altres novetats.
L'accés a una variable MVar és d'avaluació tardana, per tant el contingut serà avaluat en el consumidor i no en el productor!!.
La versió d'avaluació estricta Control.Concurrent.MVar.Strict del paquet strict-concurrency pretén evitar pèrdues d'espai forçant l'avaluació a Head Normal Form en dipositar un objecte en una MVar (putMVar).
Els fils blocats es desperten per ordre de suspensió (FIFO)."putMVar mvar" desperta el primer dels fils que ha cridat takeMVar amb la mateixa mvar, que s'emporta el valor i la deixa buida.

#### TMVar de Control.Concurrent.STM.TMVar

    MVar amb protecció transaccional (Software Transactional Memory)
   * takeMVar mvar, si la variable és buida, en comptes de blocar provoca retry transaccional
   * putMVar mvar valor, si la variable és plena, en comptes de blocar provoca retry transaccional

### bústies de comunicació amb MVar, Chan, BoundedChan

Canals amb sincronització per baldes (ang: locks).
* MVar: bústies d'un sol element (de Control.Concurrent)
* Chan: bústies amb cua il·limitada (de Control.Concurrent.Chan)
* BoundedChan: bústies limitades (del paquet BoundedChan)
Exemples:
* #Concurrència simple amb MVars - Productor-consumidor
* i afegint-hi un canal asíncron a #Client-servidor - Cues d'entrada (Chan)

### forkOS: llançament de fils d'execució lligats al del sistema (Bound threads)

Creació de fils d'execució per a crides FFI a biblioteques externes o a biblioteques amb allotjament local al fil.
GHC assigna habitualment els fils d'exec. lleugers de la multitasca GHC llançats amb forkIO, en relació N-M amb els fils del sistema (hi ha un fil del sistema per cada processador elemental, anomenats capability sobre els quals s'executen els llançats amb forkIO).
forkOS: Per poder fer ús de característiques específiques dels fils d'execució del sistema, forkOS facilita el llançament d'un fil d'execució en relació 1-1 amb els del sistema (Fils d'execució "lligats als del sistema" (Bound threads)), facilitant l'ús de crides externes (FFI: Foreign Function Interface) en un fil, o bé l'ús d'allotjament lligat al fil d'execució. Biblioteques com OpenGL requereixen aquest ús.

### modalitats de llançament

Vegeu ref.
* llançament de fil lleuger (els del planificador RTS):
* llançament de fil lligat al del sistema:
* llançament de fil d'execució indicant numèricament el processador (capability) on cal executar-lo:
* llançament de fil d'exec. amb funció per executar en acabar:

### memòria transaccional: variables TVar, i comunicació amb TMVar i TChan

* les TVar són variables de "Memòria transaccional per programari" de l'anglès "Software transactional memory" conegut per la sigla STM.
* TMVar i TChan són els equivalents transaccionals de MVar i Chan
Vegeu exemple #Concurrència condicionada amb TVars - Mònada STM - Memòria transaccional

### Futurs

Paral·lelització i espera d'accions o càlculs llançats asíncronament.

#### Encadenament de càlculs asíncrons - la mònada Par

Permet combinar càlculs funcionals purs en paral·lel, encadenant resultats com a paràmetres de paral·lelitzacions subseqüents. Vegeu refs.
* La classe ParFuture especialitza una mònada modelant el comportament d'un objecte Futur: un objecte que havent engegat una acció asíncronament, possibilita l'obtenció del resultat posant el fil d'execució original en espera, fins que l'acció asíncrona acabi.
* La classe ParIVar especialitza la ParFuture modelant una Promesa: una especialització escribible d'un Futur que possibilita resoldre l'acció asíncrona assignant un resultat.
* El tipus (IVar a) és el de les variables d'assignació única, accessibles des de la tasca asíncrona i l'original.
* El tipus (Par (Ivar a)) instancia les classes ParFuture i ParIvar
Per a càlculs que poden llançar excepcions, la biblioteca Async ofereix més varietats de tractament.

#### Operacions d'Entrada/Sortida asíncrones i simultànies

Amb la biblioteca Async. -- Futurs com a efecte global IO, amb implementació com a functor aplicatiu en el tipus Concurrently (implementa les classes Applicative i Alternative).

##### operacions asíncrones

* withAsync llança una operació IO asíncronament (sense esperar-ne la finalització) en un fil d'execució nou i n'assegura la terminació (basat en bracket sobre el recurs fil d'execució (obtenció de recurs: async io) (alliberament de recurs: cancel)).
   * wait async atura el fil actual en espera que s'acabi l'op. asíncrona designada per async, oferint-ne el resultat, i propagant-ne l'excepció en cas que n'hi hagi. Si hi ha hagut excepció al fil asíncron serà rellançada al fil actual.
   * waitBoth espera el resultat de dues accions asíncrones.
   * waitEither espera el resultat de la primera que acabi; waitEitherCancel cancela la restant
   * waitAny [Async a] espera la primera que acabi d'una llista d'accions asíncrones; waitAnyCancel cancela les restants
Hi ha versions amb el sufix Catch com waitCatch per caçar l'excepció del fil asíncron, retornant el resultat o l'error en un tipus Either, en comptes de rellançar l'excepció.

##### combinadors d'operacions simultànies

A la mateixa biblioteca async:
* concurrently simultaneja dues operacions, i en retorna ambdós resultats.
* race simultaneja dues operacions retornant el resultat de la primera que acabi i avortant la restant.
* mapConcurrently mapeja una funció amb efectes col·laterals (a -> IO b) a una col·lecció de valors, i les executa simultàniament, retornant-ne la col·lecció de resultats.

##### L'efecte Concurrently

A més alt nivell el tipus Concurrently embolcalla l'operativa precedent en la implementació de les classes Applicative (aplicant l'esmentat combinador concurrently) i Alternative (aplicant race, per esperar la primera que acabi, cancel·lant les altres).

### model d'àlgebra de processos CSP
#### Communicating Haskell Processes

La biblioteca "Communicating Haskell Processes" de la universitat de Kent implementa una àlgebra de processos CSP, basada en mònades.
Aquesta biblio. consta de diversos paquets. Cal el paquet chp-plus del Hackage per fer córrer els exemples de les guies.
Si p i q són processos, s'hi defineix, entre d'altres, els següents operadors de composició:
El codi (per a GHC 6.10) ha quedat una mica desfasat. Per compilar a GHC 7.4 cal solucionar ambigüitats per la col·lisió de símbols afegits a les noves versions de les biblioteques emprades i un símbol desaparegut de les noves versions de QuickCheck que es troba a la versió 2.3.* Passos:

### model d'Actors

El model d'actors evita la problemàtica dels blocatges, mitjançant la concurrència per pas de missatges, aïllant les estructures compartides en processos indepentdents anomenats actors que en gestionen l'estat.

#### la biblio Actor

Implementa actors multi-canal despatxant, a la recepció, en comptes de per missatge, per llista de missatges de canals diferents.
Està en codi Haskell98 i caldrà seguir les instruccions a Compilador Haskell de Glasgow#Compilar codi antic Haskell98 amb GHC.

#### Cloud Haskell

Concurrència per pas de missatges per a sistemes distribuïts, a l'estil de l'Erlang. Aplica el model d'Actors als processos distribuïts de manera similar a les construccions del llenguatge i sistema concurrent distribuït de nodes de l'Erlang.

## Excepcions asíncrones

Les excepcions asíncrones, llançades des d'altres fils al propi, amb throwTo o bé killThread, tenen la seva problemàtica. La biblioteca safe-exceptions aporta una millora, discriminant el tractament de les síncrones i les asíncrones.

## Excepcions no tractades en els fils subordinats

Les excepcions en els fils que hem llançat, que salten sense ésser tractades, es poden recollir, en el fil d'exec. pare, mitjançant setUncaughtExceptionHandler.
Vegeu exemple a "The Unhandled Exception Handler".

## Paral·lelisme
### Primitives de paral·lelisme - Compilació per a processadors multicor

Vegeu

#### par

par:: a → b → b —activa el càlcul del primer operand en paral·lel (que s'encua en espera d'una CPU disponible) mentre que el segon s'executa al fil d'exec. actual, retornant el resultat d'aquest darrer.

#### pseq

pseq:: a → b → b —avalua el primer operand en el fil d'exec. actual, de manera primerenca (estricta) i avalua el segon de manera tardana (lazy) quin resultat retorna. Vegeu "seq vs. pseq"

#### opció multiprocessador

L'opció -threaded de "ghc --make" relliga el programa amb la biblio. del Run Time System multiprocessador, emprant diversos fils d'execució del sistema per possibilitar el paral·lelisme, altrament el relliga amb la de l'RTS uniprocessador.
GHC conté un planificador de fils d'execució lleugers, llançats amb forkIO, que s'executen amb relació "M a N" sobre fils d'execució del sistema (1 per cada processador elemental present) anomenats Capability.
./parfib +RTS -s -N2

#### Estratègies

Les estratègies són funcions de coordinació de l'execució. Vegeu també
Per assegurar la constància dels resultats de les operacions, executades amb par i pseq, possiblement en fils d'execució paral·lels, convé seqüenciar-ne les operacions per garantir-ne els resultats. La mònada Eval serveix per aquest fi.
Estratègies típiques:
* parMap: combinador típic, (parMap estrat f llista) aplica (map f) a llista i avalua la llista resultant amb l'estratègia (parList estrat).
    Compte! parMap no s'ha d'utilitzar amb l'estratègia rpar, ja que activaria l'execució paral·lela doblement per cada element
Càlcul típic amb estratègies.
compilació i exec.
Exemple: #Reducció en paral·lel mitjançant estratègies.

### Paral·lelisme de dades

Tractament paral·lel de dades basat en biblioteques ad-hoc.
* procés paral·lel de vectors pluridimensionals
* "Data parallel haskell"
* Ús de les instruccions vectoritzades SIMD (Single Instruction Multiple Data) de les CPU.
* Paral·lelisme GPGPU
Vegeu Compilador Haskell de Glasgow#Paral·lelisme de dades

## Exemples de concurrència
### Concurrència simple amb MVars - Productor-consumidor

Amb variables de sincronització per baldes (blocants) MVar.

#### forkIO

    Engega fil d'execució lleuger del planificador del Haskell en multiprocés cooperatiu.

#### putMVar mvar valor

    bloca el fil d'execució si la variable MVar és plena (ocupada) fins que estigui disponible (buida); llavors l'omple amb el valor i continua.

#### takeMVar mvar

    bloca el fil d'execució si la variable MVar és buida fins que la li omplin; llavors en recupera el valor, la deixa buida i continua.
produeix la sortida següent:

### Client-servidor - Canals amb cues d'entrada (Chan)
#### Client-servidor, canalitzant la impressió

Canals no acotats (en la dimensió de la cua) (Control.Concurrent.Chan)"de primera classe" (és a dir, que es pot passar com a paràmetre)
* forkIO: Engega fil d'execució lleuger del planificador del Haskell.
* forkOS: engega un fil lligat a un del sistema operatiu, per al cas de voler utilitzar característiques dels fils d'exec. del sistema, com ara crides FFI a biblioteques del sistema o bé allotjament lligat al fil.
* comunic. per cues il·limitades

   #### writeChan canal

       afegeix a la cua il·limitada i retorna tot seguit (sense blocar) (comunicació asíncrona).

   #### readChan canal

       bloca si la cua del canal és buida
* resposta per MVars
* semàfors:

   #### newQSem valorInicial

       nou semàfor

   #### signalQSem semàfor

       incrementa semàfor i assenyala

   #### waitQSem semàfor

       si semàfor > 0 llavors decrementa i continua, sinó espera
* A l'exemple el client encua al canal torn, el parell (comanda, ref. resposta (mv_resposta)), i queda a l'espera de la resposta.
dona la següent sortida:

### Concurrència condicionada amb variables transaccionals (TVar) - Mònada STM (transaccions de memòria per programari)

Només al compilador GHC. Les transaccions de memòria eviten blocar els fils d'execució, descartant canvis a les variables transaccionals si no es completa, excepte en cas que hi posem condicions forçant el reintent amb la clàusula "retry".
L'evolució de la transacció es modela com a aplicacions en una Mònada STM, inicials de "Software Transactional Memory".
En aquest exemple les transaccions s'efectuen sobre variables transaccionals TVar (accés sincronitzat per STM) i s'encapsulen en una mònada STM.
També substituïm les MVar (comunic. síncrona) per TMVar, i les Chan (comunic. asíncrona) per TChan, per quedar lliures de problemes de bloquejos.

#### atomically

    avalua una expressió de la mònada STM, admetent o tot o no res dels canvis a les variables transaccionals, oferint el resultat com a efecte global (mònada IO).

#### retry

    provoca el reintent si no es donen les condicions esperades i reintenta una transacció alternativa per la branca "orElse" si existeix, i si no, bloca el fil d'execució fins que es modifiqui alguna de les variables transaccionals implicades en la transacció.

#### orElse

    introdueix una transacció alternativa, que s'avalua si la precedent fa "retry"

#### always

    comprova invariant i si falla, dispara un error "Transactional invariant violation" finalitzant el programa

#### catchSTM

    atrapa excepcions dins la mònada STM
A partir de GHC 6.12, STM desapareix de la biblioteca pral. i, si no s'ha fet la instal·lació amb la Plataforma Haskell que l'incorpora, caldrà carregar el paquet stm del Hackage.
* La concurrència a l'exemple utilitza la biblioteca Async:

   #### withAsync acció_io $ \ async -> altres accions al fil d'exec. original

       llença l'acció_io en un altre fil d'execució, asíncronament i continua amb les altres accions

   #### wait async

       espera finalització del fil d'exec. llançat asíncronament, identificat per async i en retorna el resultat
Principal engegant fils d'execució per a creditor, deutor i informador (que gestiona stdout).
Compilació i exec.

## Referències

* ↑ «mòdul Control.Concurrent Vegeu Scheduling». Arxivat de l'original el 2012-08-02. [Consulta: 1r desembre 2011].
* 1 2 GHC's implementation of concurrency Arxivat 2012-08-02 at Archive.is(anglès)
* ↑ Control.Concurrent.MVar(anglès)
* ↑ Control.Concurrent.Chan(anglès)
* ↑ Control.Concurrent.STM.TVar(anglès)
* ↑ Control.Concurrent.STM.TMVar(anglès)
* ↑ Control.Concurrent.STM.TChan(anglès)
* 1 2 3 4 Variables MVar(anglès)
* ↑ Control.Concurrent.MVar.Strict(anglès)
* 1 2 3 El planificador del GHC - fils d'exec. del sistema i fils del GHC (anglès)
* ↑ «Concurrency – Haskell Prime». Arxivat de l'original el 2011-12-09. [Consulta: 1r desembre 2011].(anglès)
* 1 2 3 Bound threads (fils d'execució lligats als del sistema)(anglès)
* ↑ Concurrència i crides externes (FFI) al compilador GHC(anglès)
* ↑ Control.Concurrent.forkIO(anglès)
* ↑ Control.Concurrent.forkOS(anglès)
* ↑ Control.Concurrent.forkOn(anglès)
* ↑ Control.Concurrent.forkFinally(anglès)
* ↑ La mònada Par - presentació Arxivat 2013-08-23 a Wayback Machine. (anglès)
* ↑ La mònada Par(anglès)
* ↑ «Haskell Exchange 2012 - Simon Marlow - High performance concurrency». Arxivat de l'original el 2013-02-03. [Consulta: 18 gener 2013].
* ↑ El paquet Async
* ↑ mòdul Control.Concurrent.Async combinadors(anglès)
* ↑ mòdul Control.Concurrent.Async newtype Concurrently(anglès)
* ↑ CHP.pdf(anglès) Communicating Haskell Processes: Composable Explicit Concurrency Using Monads
* ↑ Univ. de Kent - Communicating Haskell Processes - pàgina inicial
* ↑ paquet chp
* ↑ Actors with Multi-Headed Receive Clauses (anglès)
* ↑ Cloud Haskell(anglès)
* ↑ A Cloud Haskell Appetiser(anglès) Un aperitiu de Cloud Haskell
* ↑ Vídeo: HIW 2012. Duncan Coutts: Cloud Haskell 2.0(anglès)
* ↑ Control.Concurrent.throwTo(anglès)
* ↑ safe-exceptions - README(anglès)
* ↑ GHC.Conc.Sync.setUncaughtExceptionHandler(anglès)
* ↑ The Unhandled Exception Handler Arxivat 2012-09-06 a Wayback Machine.(anglès)
* ↑ primitives de paral·lelisme(anglès)
* ↑ «Haskell Paral·lel». Arxivat de l'original el 2014-10-25. [Consulta: 18 agost 2017].
* ↑ GHC seq vs. pseq(anglès) Comparació de les primitives seq i pseq
* ↑ GHC - primitiva pseq(anglès)
* ↑ «GHC - Utilitzant multiprocés simètric SMP». Arxivat de l'original el 2014-09-27. [Consulta: 18 agost 2017].
* ↑ GHC - Hints (cat:Pistes) for using SMP parallelism Arxivat 2014-09-27 a Wayback Machine. (anglès)
* 1 2 Glasgow Parallel Haskell (anglès) Estratègies de paral·lelisme
* 1 2 Control.Parallel.Strategies(anglès)
* ↑ Seq no more: Better Strategies for Parallel Haskell Arxivat 2017-08-13 a Wayback Machine.(anglès) Prou de seq (seqüencial) - millors estratègies per al Haskell paral·lel
* ↑ haskellWiki - paral·lelisme(anglès)
* ↑ parMap
* ↑ Variables de sicronització MVar's Arxivat 2010-04-17 a Wayback Machine. (anglès)
* ↑ Control.Concurrent.Chan Arxivat 2014-07-15 a Wayback Machine. (anglès)
* ↑ Communicating .. - First Class Channels Arxivat 2010-07-06 a Wayback Machine.(anglès)
* ↑ API de concurrència del compilador GHC Arxivat 2011-11-19 a Wayback Machine. (anglès)
* ↑ HaskellWiki - STM - Transaccions de memòria per software (anglès)
* ↑ Variables transaccionals
* ↑ Avaluació de models de programació multicor (PDF)(anglès)
* ↑ Què se n'ha fet de Control.Concurrent.STM(anglès)
* ↑ mòdul Control.Concurrent.Async(anglès)

## Enllaços externs

* Haskellwiki - Concurrència
* Haskellwiki - Paral·lelisme
* The Unhandled Exception Handler Arxivat 2012-09-06 a Wayback Machine. (anglès) De com recollir al fil d'exec. principal les excepcions no caçades als fils d'execució fills.