[#1] input.device i ringbuffer
Hejka programiści!

Mam pytanie: czy handlery input.device wywoływane są w przerwaniu? Chciałbym zapisywać naciskane klawisze w tablicy i chcę być pewnym, że nie muszę używać semaforów (Semaphore) lub komunikatów (PutMsg) by zagwarantować wyłączny dostęp do tej tablicy.

Dzięki za odpowiedź i pozdrawiam.
[#2] Re: input.device i ringbuffer

@Hexmage960, post #1

Jezeli handlery bylyby wywolywane w przerwaniu, to nie bylo by dyskusji czy musialbys uzywac semaforow albo nie. Przy obsludze przerwania nie masz prawa uzywac semaforow. Musialbys blokowac dostep do tablicy za pomoca Disable()/Enable(). Ale ogolnie tak, zagwarantuj sabie wylacznosc na dostep do twojej tablicy...

ZTCP handlery sa wolane poza przerwaniem ale za to na tasku input.device z wysokim priorytetem. Ale moge sie mylic...
[#3] Re: input.device i ringbuffer

@mschulz, post #2

Przy obsludze przerwania nie masz prawa uzywac semaforow.

Ja to wiem, że w przerwaniu nie wolno czekać, a tym samym używać semaforów.

Jednak zauważ, że jeśli handlery input.device są wywoływane w przerwaniu, to nie muszę stosować semaforów w ogóle, bo kod przerwania wykonywany jest w całości przez procesor.

Jeżeli zaś kod nie jest wywoływany w przerwaniu, wtedy te semafory trzeba zastosować by przypadkiem kod handlera i mój nie korzystały z tej tablicy równocześnie.
[#4] Re: input.device i ringbuffer

@Hexmage960, post #3

Jednak zauważ, że jeśli handlery input.device są wywoływane w przerwaniu, to nie muszę stosować semaforów w ogóle, bo kod przerwania wykonywany jest w całości przez procesor.


Musisz ochronic swoja strukture jezeli nie mozesz zagwarantowac wylacznosci dostepu do niej. Jezeli handler bylby wywolywany w przerwaniu, musialbys dostep do struktury obwarowac za pomoca Disable()/Enable(). Wyobraz sobie nastepujaca systuacje:

1. Twoj program zaczyna korzystac ze struktury (jezeli to naprawde ringbuffer to czyta head do rejestru, czyta tail to rejestru)
2. W tym czasie wygenrowane zostaje przerwanie, twoj handler zapisuje dane do bufora, aktualizuje head). Przerwanie sie konczy
3. Twoj program kontynuuje prace, tyle ze uzywa wskaznika head ktory trzymal w rejestrze zanim ten zostal zaktualizowany w przerwaniu -> bum, dane stracone albo struktura zepsuta.

poprawna wersja:

1. Twoj program wola Disable() zaczyna korzystac ze struktury.
2. Wygenerowane zostaje przerwanie ale jest jeszcze zamaskowane, zaden handler nie startuje.
3. Twoj program konczy korzystac ze struktury. Wola Enable()
4. W tym momencie rusza handler obslugujacy przerwanie, handler korzysta ze struktury nie psujac nic po drodze.


Dyskusja jest czysto akademicka, jednak:
1. Jezeli kilka taskow moze korzystac z jednej struktury danych w tym samym czasie, uzyj semaforow. Handlery z input device do tej kategorii sie na 99.99% zaliczaja
2. Jezeli jeden lub kilka taskow oraz kod w trybie supervisor moze korzystac z jednej struktury w tym samym czasie, uzyj Disable()/Enable().
3. Nie uzywaj Forbid()/Permit()! To zlo wcielone!
[#5] Re: input.device i ringbuffer

@mschulz, post #4

Słusznie zauważyłeś, że przerwanie może być wykonane w każdej chwili, również podczas obsługi tablicy przez program główny.

Możemy zrobić tak:

Inicjalizacja:
Zmienne współdzielone:
indeks A = 0; Pierwsze nieodczytane miejsce w ringbufferze
indeks B = 0; Liczba wczytanych znaków w ringbufferze
indeks C = 0; Aktualna kopia B.

semafor K = 0; Znacznik wykonania kopii tablicy/aktualizacji liczników

Procedura handlera:

Sekcja krytyczna. Wykonywana tylko gdy program na to pozwoli, ale handler na to nie czeka.

- Jeżeli wykonano już kopię tablicy kodów klawiszy (co można określić po stanie semafora K - ten znacznik musi być zapalony), zaktualizuj informację o wolnym miejscu w tej tablicy (wartość A zwiększ o C, a następnie zmniejsz wartość B o wartość C).
Następnie skopiuj B do C.
Po czym zgaś znacznik K.

Koniec sekcji krytycznej.

- Dopisz kody naciśniętych klawiszy w wolne miejsce tablicy (A + B), (A + B + 1), (A + B + 2), (A + B + 3)..., (A + B - 1). W przypadku dojścia do końca tablicy, przejdź na jej początek. W przypadku przepełnienia nie dopisuj nowych znaków.
- Aktualizuj wielkość zapełnienia tablicy, poprzez aktualizację wartości B. Zwiększ go o liczbę pomyślnie pobranych klawiszy.

Procedura główna:

Sekcja krytyczna (wykonywana tylko, gdy kod handlera na to pozwoli):
- Czekaj, aż znacznik K zostanie zgaszony (korzystając z semafora),

- Pobierz jaka w tym momencie jest wielkość zapełnienia tablicy (wartość C).

- Stwórz lokalną kopię tablicy od A, (A + 1), (A + 2), (A + 3),..., (A + C - 1).

- Zapal znacznik K wykonania takiej kopii.
Koniec sekcji krytycznej.

Chyba jest OK.

P.S. Poprawka ze współdzieleniem B i C.

Ostatnia aktualizacja: 21.06.2018 21:08:28 przez Hexmage960
[#6] Re: input.device i ringbuffer

@Hexmage960, post #5

Chyba jest OK.


Pozwol ze zacytuje samego siebie:


1. Jezeli kilka taskow moze korzystac z jednej struktury danych w tym samym czasie, uzyj semaforow. Handlery z input device do tej kategorii sie na 99.99% zaliczaja
2. Jezeli jeden lub kilka taskow oraz kod w trybie supervisor moze korzystac z jednej struktury w tym samym czasie, uzyj Disable()/Enable().


w przypadku punktu 1 potrzebujesz funkcji:
- InitSemaphore
- ObtainSemaphore
- ReleaseSemaphore
- (opcjonalnie) ObtainSemaphoreShared

w przypadku punktu 2 potrzebujesz funckji:
- Disable
- Enable

I tyle. To co ty proponujesz to jest wymyslanie kola na nowo. Po co? AmigaOS daje ci wszystkie mechanizmy ktorych moglbys tutaj potrzebowac. Skorzystaj z nich. Nie wymyslaj wlasnej implementacji bo sie na tym mozesz niezle przejechac. A jezeli potrzebujesz tego w ramach samoszkolenia to zaprogramuj mechanizmy synchronizujace a potem z nich skorzystaj a nie na odwrot.

PS. m68k jest dosc biedny pod tym wzgledem. Ma w sumie tylko instrukcje TAS. W PPC, ARM, x86 jest o wiele zabawniej.
[#7] Re: input.device i ringbuffer

@Hexmage960, post #5

Możemy zrobić tak:


Jak bardzo chcesz sie doszkolic to zajzyj tutaj (i w sumie do wszystkich innych dotyczacych spinlockow):
https://github.com/michalsc/AROS/blob/master/arch/all-pc/kernel/spinlock.c
https://github.com/michalsc/AROS/blob/master/arch/all-pc/kernel/spinunlock.c

milego grzebania w kodzie....
[#8] Re: input.device i ringbuffer

@mschulz, post #6

Hej, ale nie ustaliliśmy jeszcze, czy handler input.device jest wywoływany w przerwaniu. Stąd nie wiadomo, czy Enable/Disable zadziała.

Ja chcę skorzystać z mechanizmów AmigaOS. W moim przykładzie używam semafora, który pewnie można zaimplementować za pomocą semaforów z exec.library.

Poprawki do mojego kodu:
- Ostatnim miejscem, gdzie można wklejać nowe klawisze jest (A - 1), a nie (A + B - 1).
- Zapomniałem o tym wspomnieć, ale oczywiście wszystkie miejsca w tablicy są liczone modulo rozmiaru tablicy (bo to ringbuffer).

Ostatnia aktualizacja: 22.06.2018 04:00:52 przez Hexmage960
[#9] Re: input.device i ringbuffer

@Hexmage960, post #8

Hej, ale nie ustaliliśmy jeszcze, czy handler input.device jest wywoływany w przerwaniu.


Jak juz powiedzialem jestem na 99.9% pewien ze handlery z input device nie sa wolane w przerwaniu tylko z poziomu tasku input.device. tego z dosc wysokim priorytetem.

Stąd nie wiadomo, czy Enable/Disable zadziała.


Disable()/Enable() dziala zawsze. Jest po prostu najostrzejszym mechanizmem blokujacym, jedynym dzialajacym takze w trybie supervisor. Dlatego powinien byc stosowany w ostatecznosci.
[#10] Re: input.device i ringbuffer

@mschulz, post #9

Jak juz powiedzialem jestem na 99.9% pewien ze handlery z input device nie sa wolane w przerwaniu tylko z poziomu tasku input.device. tego z dosc wysokim priorytetem.

Też tak myślę. Kiedyś robiłem handlery input.device z AllocMem() i w praktyce nie było problemów. Teraz jednak chciałem zastosować ringbuffer, by skrócić i przyśpieszyć kod.
[#11] Re: input.device i ringbuffer

@Hexmage960, post #10

Zdecydowałem się w końcu na PutMsg() ze względu na niebywałą elegancję tego rozwiązania. W pętli głównej odbieram wiadomości za pomocą GetMsg().

Alokuję wiadomości dynamicznie, choć statycznie też można.

Dzisiaj z pół godziny zastanawiałem się czemu mi nie działa kod, który wyglądał na 100% poprawny.

W końcu sprawdziłem inny kompilator. Okazało się, że DICE nie najlepiej poradził sobie z załączeniem binarek z PhxAss (handle input.device napisałem w asemblerze).

Ten inny kompilator to MaxonC++, który zadziałał bez problemu i zostaję przy nim, bo w sumie jest bardzo dobry i śmiga na Amidze.
[#12] Re: input.device i ringbuffer

@Hexmage960, post #11

Hej, chciałem tylko podsumować ten wątek kodem z zastosowanym statycznym buforem "pierścieniowym".

Ten kod zapamiętuje wydarzenia typu IECLASS_RAWKEY (klawiatury) w statycznej strukturze podanej jako Data (w rejestrze adresowym A1). Następnie umieszcza tę strukturę w porcie komunikacyjnym za pomocą PutMsg(), który przy okazji sygnalizuje task główny. Jak wiadomo PutMsg() nie kopiuje zawartości wiadomości.

Definicja struktury znajduje się w Joy.h (język C) oraz Keyboard.s (asembler).

W przypadku klawiatury nie ma zabezpieczenia w formie semafora. Bufor jednak przechowuje maksymalnie MAX_EVENTS wydarzeń, co i tak jest zbyt dużym zapasem.

Ważne:
  • indeks wydarzeń "counter" przyjmuje kolejne cyfry 0, 1, 2, 3, ..., (MAX_EVENTS - 1) i znów 0, 1, itd. i wskazuje na miejsce gdzie będzie zapisane nast. wydarzenie. Czytamy zatem wydarzenia od ostatnio odczytanego do "counter" (nie wliczając go).
  • Wiadomość nadchodzi tylko, gdy co najmniej jedno wydarzenie zostało zanotowane.
  • Priorytet handlera jest ustawiony na 100 (żeby było wyżej niż intuition, który ma 50).
  • Handler nie przekazuje niżej wiadomości, zatem przechwytuje wszystkie.

Przetestowałem ten kod i działa.

Do wglądu oto pełne funkcje obsługujące klawiaturę (oraz również joystick) za pomocą input i gameport.device:

http://coreprogramming.pl/Magazyn/joy_c.txt
http://coreprogramming.pl/Magazyn/joy_h.txt
http://coreprogramming.pl/Magazyn/keyboard_s.txt

Może komuś się przyda do własnych produkcji.

Ostatnia aktualizacja: 05.01.2019 20:48:20 przez Hexmage960
[#13] Re: input.device i ringbuffer

@Hexmage960, post #12

Dość istotna nota: funkcja keyboardOpen() (a w zasadzie mój handler input.device) zakłada, że w polu port struktury inputDataMessage jest wstawiony wskaźnik utworzonego w programie głównym portu komunikacyjnego za pomocą CreateMsgPort().

Przed wywołaniem keyboardOpen() trzeba zatem utworzyć port i go tam wstawić.

Do tego portu będą napływały wiadomości o wciskanych klawiszach, które należy pobierać za pomocą GetMsg(). Zawierają one jedną i tę samą (ale ze zmienianą zawartością) strukturę inputDataMessage. Struktura jest deklarowana statycznie (tak naprawdę to ta sama struktura przekazywana do keyboardOpen()), więc jej nie zwalniamy.

Ostatnia aktualizacja: 05.01.2019 21:45:58 przez Hexmage960
Na stronie www.PPA.pl, podobnie jak na wielu innych stronach internetowych, wykorzystywane są tzw. cookies (ciasteczka). Służą ona m.in. do tego, aby zalogować się na swoje konto, czy brać udział w ankietach. Ze względu na nowe regulacje prawne jesteśmy zobowiązani do poinformowania Cię o tym w wyraźniejszy niż dotychczas sposób. Dalsze korzystanie z naszej strony bez zmiany ustawień przeglądarki internetowej będzie oznaczać, że zgadzasz się na ich wykorzystywanie.
OK, rozumiem