|
![]() |
AmigaDev Parte nona: Alberi di macro e condivisione di dati Un programmatore ARexx smaliziato viene prima o poi a contatto con un grave deficit di ARexx: la mancanza di qualsiasi struttura di controllo di alberi di macro e l'impossibilità di condividere i dati tra macro. Dedichiamo questa intera puntata ad illustrare come risolvere entrambi i problemi. Progetti non banali sono caratterizzati dalla seguente struttura: una macro detta padre, lancia una o più macro dette figlie. Il padre deve potere controllare l'esecuzione delle macro figlie, sincronizzandole o, più frequentemente, interrompendole quando desidera. Il padre deve potere passare alle macro figlie un insieme di dati su cui operare o, addirittura, esiste un insieme di dati che deve essere a disposizione e modificabile da qualsiasi macro figlia. Ebbene, l'interprete ARexx non fornisce alcun metodo per realizzare tutto ciò. L'unico modo di lanciare macro è di chiamarle attraverso il call od eseguirle come camando shell con shell command 'rx nome_macro'. L'unico modo di condividere dati è la lista delle clips; purtroppo non esiste alcun meccanismo per cancellare le clips create da una macro alla sua uscita, il che significa che, dopo un po', la lista delle clips è talmente sporca e la memoria allocata talmente grande da non permetterne un uso pulito. Dopo ampia riflessione, sono giunto alla conclusione che nuovi metodi e concetti fossero necessari: il risultato sono MacroNotify, NamedSpace e variabili locali. MacroNotify Un MacroNotify è un handler trasparente al programmatore, che identifica
uno spazio di macro. Con spazio di macro si intende un club, che ha
un proprietario e regole precise. L'esistenza di un MacroNotify è strettamente
legata all'esecuzione della macro in cui è stato creato: all'uscita
della macro il MacroNotify cessa di esistere. msn = MacroNotifyCreate(name)name deve essere un nome unico: per ottenerlo esistono molti metodi, ad esempio name=Pragma("ID"). Il risultato della funzione, mns, è un segnale Exec da attendere per ricevere la notifica "un evento è accaduto sul MacroNotify". In una macro, possono essere creati un qualsiasi numero di MacroNotify, anche se nella maggior parte dei casi, se ne creerà soltanto uno. Quando una macro figlia viene lanciata dalla macro padre, deve unirsi esplicitamente al club con call MacroNotifyJoin(name).Poiché tutto ciò abbia senso, occorre che la macro figlia sia stata lanciata dal padre in modalità asincrona. Infatti se lanciassimo macro in modalità sincrona, non potremmo avere alcun controllo su di esse, per la semplice ragione che saremmo bloccati ad attendere la loro esecuzione. Nella pratica ciò si traduce nell'impossibilità di usare i comandi call e shell command per lanciare le macro figlie e nell'esigenza di trovare un meccanismo più raffinato. Fortunatamente, RxSocket mette a disposizione la funzione RxsCall() che permette, tra le altre cose, di lanciare macro in modalità asincrona. Finalmente unitasi al club, una macro figlia è sotto controllo da parte della macro padre, perché:
padre: name = Pragma("ID") msn = MacroNotifyCreate(name) … call rxscall(macro_figlia name) … figlia: … call MacroNotifyJoin(name) …Ad un MacroNotify sono associati gli eventi:
… mask = or(mns,…) … recv=wait(mask) if and(recv,mns)>0 then call HandleMAcroNotifyEvents … MacroNotifyEvents: do forever ev=MacroNotifyNexEvent(name) if ev="" then return parse var ev ev id more select when ev="STARTED" then do /* macro id started */ end when ev="ENDED" then do /* macro id ended */ end when ev="INFO" then do /* macro id sends us infos, more is an info string */ end when ev="ATTEMPT" then do /* macro id tried to create the MacroNotify */ end endIl formato dei vari eventi è:
… Signal on break_d … break_d: exitOppure può avere la prima volta il significato di ok, vai! e la seconda di muori e così via. Faccio notare che anche se MacroNotifySync() permette soltanto di "brekkare" tutte le macro figlie, conservando gli id tornati dall'evento STARTED, potrete in qualsiasi momento "brekkare" selettivamente qualsiasi macro con call Signal(x2d(id),2**12). Si rammenti che una macro figlia fa parte del club solo nel momento in cui il padre riceve l'evento STARTED, e non al suo lancio. I MacroNotify permettono di creare alberi di macro di qualsiasi natura e di avere totalmente sotto controllo la dinamica con cui questi si sviluppano. L'unica pecca dei MacroNotify è la seguente: uno schema molto diffuso è quello in cui la macro padre lancia la figlia e si mette in attesa di ricevere sul MacroNotify l'evento STARTED; se la macro figlia viene interrotta dall'esterno (ad esempio perché riceve un halt), prima di aver usato MacroNotifyJoin(), la macro padre è destinata ad attendere per sempre. Il rimedio a ciò è il seguente: la macro figlia deve intrappolare qualsiasi interruzione (break_c halt e così via) e chiamare MacroNotifyJoin() al ricevimento di un'interruzione, se non ancora fatto. Condivisione dati I casi che si presentano sono svariati, ma riconducibili ai due seguenti:
Ogni processo Amiga conserva una lista di coppie <nome, valore>
dette variabili locali. Tali variabili possono essere passate da un
processo ad un altro al momento della creazione. Esattamente queste
variabili sono, ad esempio, usate dai server HTTP per passare importanti
dati alle macro CGI. var.Host var.HostPort var.Path var.User var.Pass var.Referer var.Resume var.IfModifiedSinceQuesti sono i tipici dati che una macro per scaricare file da un server HTTP usa. Poiché in qualsiasi momento, nella macro padre, questi dati possono cambiare, dobbiamo sincronizziamo le nostre variabili locali con lo stem var. esattamente prima di chiamare la macro figlia: call StemToVar("var") call RxsCall("child_macro")Appena parte, la macro figlia, copia le sue variabili locali in uno stem con call VarToStem("var.#?")e il gioco è fatto, ovvero la macro figlia riceve con un sol colpo (una sola chiamata a VarToStem()) un grande insieme di dati. Pensate che questo "giochino" risolve l'annoso problema di condividire tra macro un intero stem ARexx. Il problema è, al solito, che né call né rx copiano le variabili locali, quindi dovrete sempre usare RxsCall() oppure rxs per lanciare le macro. E' consigliabile creare contemporaneamente un MacroNotify e passare il suo nome in una variabile locale. Anche se una macro figlia ha bisogno di un solo dato, ad esempio var.Host, conviene sempre che copi tutti i campi di var. con VarToStem(), piuttosto che leggere solo var.Host con FindVar("var.host"), poiché il sovraccarico di VarToStem() è minimo. Il modo più veloce di testare il meccanismo di condivisione dati via variabili locali è la linea: rx "do i=0 to 99; var.i=i; end; call StemToVar('var'); call RxsCall('call VarToStem(var.#?);do i=0 to 99;say var.i;end'),,'string')"NamedSpace Un NamedSpace è un handler trasparente all'utente che rappresenta uno spazio di associazioni <nome, variabile>. Un NamedSpace viene creato con res=NamedSpaceCreate(name,opt)e la sua esistenza è legata alla vita della macro in cui è stato creato. Come nel caso di un MacroNotify, il nome deve essere unico. Le opzioni sono per ora solo PRIVATE: se specificata il NamedSpace può essere modificato soltanto dal creatore. In qualsiasi momento una macro può leggere una o più variabili e, se il NamedSpace non è privato, modificarne il valore. Le funzioni che operano su un NamedSpace sono riportate in Tabella 3. Due sono notevoli: NamedSpaceExport() e NamedSpaceImport(), che permettono di sincronizzare i nostri dati con un NamedSpace in entrambi i sensi. Si noti che un NamedSpace è molto potente, ma andrebbe usato soltanto quando il meccanismo di condivisioni dati attraverso le variabili locali non è sufficiente (ciò succede quando molte macro in competizione devono poter liberamente accedere e modificare un grande insieme di dati). Varie ed eventuali Correlato agli argomenti trattati è il concetto di ambiente. Quando una macro parte, ha una nutrita serie di dati a sua disposizione, che sarebbe interessantissimo poter ricavare. Ciò è possibile attraverso la funzione MacroEnv() di rmh.library. MacroEnv() deve essere lanciata all'inizio della macro, e il suo scopo è di recuperare e impostare molti parametri legati all'ambiente in cui la macro gira. La sua sintassi è: call MacroEnv(stem,opt)MacroEnv() scrive in stem:
Tabella 1: Funzioni che operano su MacroNotify
Tabella 2: Funzioni che operano su variabili locali
Tabella 3: Funzioni che operano su NamedSpace
|
Copyright (C) 1999-2002, la redazione di AmigaLife.
Il logo e le copertine della rivista sono tratti dal sito Pluricom e sono Copyright (C) 1992-2001 Pluricom S.r.l.