LINUX.ORG.RU

Signal Handler — как сделать лучше?

 ,


0

3

Есть код, который должен возвращать промежуточный результат при keyboard interruption. Пока я реализовал топорное решение: signal handler что-то пишет в некий MVar, а код вычисления этот MVar периодически проверяет. Всё работает, но это убого.

А как бы эту задачу решил %username%? Поделитесь best practices.


use netwire!!! (шутка)

я бы делал тоже самое, но скорее с IORef, если нету ограничений, которые в условии не указаны. Щас попробую ещё набросать идиотское решение, которое может сработать.

qnikst ★★★★★
()
Последнее исправление: qnikst (всего исправлений: 1)
Ответ на: комментарий от qnikst

не сработало, в общем я вижу только мутабельную переменную в вычислении (куда пихать то, что нужно выводить или StM m), тогда её из хэндлера можно читать и выводить, или как сделал ты.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Ну у меня ещё есть смутная мысль закатать чистые вычисления в какой-нибудь Cont и написать свой runCont, который бы чекал мутабельную переменную. Так можно было бы оставить код вычислений более-менее чистым.

fmdw
() автор топика
Ответ на: комментарий от fmdw

Ну я рассматривал такой вариант как часть того, что ты описал в стартовом посте. В общем-то так оно выглядит более адекватно, но суть остается такая же. Я правильно понял, что ты хочешь сделать, что-то вроде?

data CFix a b = CFix {- state -} a  {- continuation -} !(CFix b)
              | Result b

computation :: ... -> CFix a b

runner :: Show a => CFix a b -> IO b
runner = loop
  where
    loop (Result x)  = return x
    loop (CFix s f') = do
      ifSomeStuffHappens (print s)
      loop $! f'

(интересно сколько я раз тут налажал)

qnikst ★★★★★
()
Последнее исправление: qnikst (всего исправлений: 1)
Ответ на: комментарий от quantum-troll

promise не имеет ничего общего с тем, что просилось.

Ну и чистый аналог promise это любое ленивое значение, не совсем чистый это LVar (https://hackage.haskell.org/package/lvish-1.1.4/docs/Data-LVar-IVar.html) или если в отдельном процессе считаются, то Async (https://hackage.haskell.org/package/async-2.0.1.6/docs/Control-Concurrent-Asy...).

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

promise не имеет ничего общего с тем, что просилось.

Знаю. Аналогом было бы FRP, но это немного оверкилл.

quantum-troll ★★★★★
()
Ответ на: комментарий от fmdw

Сонный мозг породил сей Лавкравтовский кошмар:

calc _ 0 = 0
calc f n = f $ n-1

fix' s f = readMVar s >>= \case 
   False -> do
     a <- unsafeInterleaveIO (fix' s f)
     return $ f a
   True -> return $ f id

tl;dr: Y-комбинатор, который умеет прерывать вычисления, когда переменная s становится True.

fmdw
() автор топика
Ответ на: комментарий от quantum-troll

к следующему шагу только тогда, когда закончиться предыдущий.

Извини, не могу дальше читать.

fmdw
() автор топика
Ответ на: комментарий от fmdw

можешь объяснить каким чудом unsafeInterleaveIO делает тут это все работать? И чего у всей этой радости будет со сложностью по памяти.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

а то моё дурацкое решение, что выше у меня отрабатывает слегка побыстрее (без оптимизаций правда). или я не правильно использую твой fix'?

{-# LANGUAGE LambdaCase #-}
import Control.Concurrent
import System.IO.Unsafe

calc _ 0 = 0
calc f n = f $ n-1

fix' s f = readMVar s >>= \case 
   False -> do
        a <- unsafeInterleaveIO (fix' s f)
	return $ f a
   True -> return $ f id

test = do
  fin <- newEmptyMVar
  x <- newMVar False
  t <- forkIO $ fix' x calc >>= print . ($150000) >> putMVar fin ()
  -- yield
  -- _ <- swapMVar x True
  takeMVar fin

data CFix a b = CFix a (a -> CFix a b)
              | Result a

calcc 0 = Result 0
calcc n = CFix (n-1) calcc

run _ (Result v) = return v
run s (CFix v f) = readMVar s >>= \case
  False -> run s (f v)
  True  -> return v

testc = do
  fin <- newEmptyMVar
  x <- newMVar False
  t <- forkIO $ run x (calcc 150000) >>= print >> putMVar fin ()
  -- yield
  -- _ <- swapMVar x True
  takeMVar fin

ghci:

*Main> testc
0
(0.27 secs, 71795496 bytes)
*Main> test
0
(0.40 secs, 143504040 bytes)
qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Это вариант unsafePerformIO, который откладывает IO действие до востребования в чистом коде. Его используют для lazy IO, например.

По поводу сложности по памяти — тут вроде не tail call, так что этот код годится только поржать.

fmdw
() автор топика
Ответ на: комментарий от fmdw

Итак, я попробовал сделать это продолжениями во имя GOTO и спагетти монстра:

calc _  done 0 = done 0
calc fp _    n = fp $ n+1

loop v f n = callCC $ \done -> loop' done (\fp -> f fp done) n
  where loop' done f' n = do
          n' <- liftIO (readMVar v) >>= \case
            False -> callCC $ \fp -> f' fp n
            True -> f' done n
          loop' done f' n'
  
setMVar v = (tryTakeMVar v >>) . putMVar v
  
main = do
  v <- newMVar False
  installHandler keyboardSignal (Catch $ setMVar v True) Nothing
  r <- evalContT $ loop v calc (7 ::Int)
  print r

Можно ли как-то улучшить этот код?

fmdw
() автор топика
Ответ на: комментарий от fmdw

Я бы предложил использовать atomic IORef, чтобы чуть меньше тратить на синхронизацию (и упростить её):

{-# LANGUAGE BangPatterns #-}
import qualified Data.IORef as DI
import qualified System.Posix.Signals as SPS

-- pure computation
data Comp a = Partial !a
            | Done a
    deriving Show

calc :: Integer -> Comp Integer
calc v =
    case v of
        0 -> Done 0
        _ -> Partial (v+1)

-- messy stuff with signals and completion
compute :: Show a => a -> (a -> Comp a) -> IO a
compute ini calc' = do
    intermediate <- DI.newIORef ini

    -- signal stuff
    _ <- SPS.installHandler SPS.keyboardSignal
                            (SPS.Catch (DI.readIORef intermediate >>= print))
                            Nothing
    let loop v = do
        case calc' v of
            Done r -> return r
            Partial pr -> do
                DI.atomicModifyIORef intermediate
                                     (\_ -> (pr, ()))
                loop pr
    loop ini

main :: IO ()
main = do
    compute 42 calc >>= print
sf ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.