Skip to content

Instantly share code, notes, and snippets.

@cr0sh
Last active March 6, 2016 12:53
Show Gist options
  • Save cr0sh/79fb54192a71cfb64d2c to your computer and use it in GitHub Desktop.
Save cr0sh/79fb54192a71cfb64d2c to your computer and use it in GitHub Desktop.
Synopsis Language draft(Korean)

Synopsis language

목적: 메모리 오류를 방지하는 동시성 프로그래밍

사전 고찰

동시성 프로그래밍에서의 위험 요소: Race conditionDeadlock

Rust 언어의 해결 방안: move-by-default, ownership, lifetime

  • = 연산자는 변수의 ownership(소유권)을 이동시킴
  • 소유권이 없는 변수는 사용 불가
  • 변수가 정의된 scope를 벗어나면 자동 해제(lifetime 소멸)
  • 역시 scope 밖에서 사용 불가
  • 모든 것이 이동이면 불편하므로 ownership borrowing(소유권 대여) 가능
  • &(Read-only)를 여러 개 빌리거나 &mut(Read-Write)를 한 개 빌릴 수 있음
  • 대여된 ownership 반환 전까지는 원본 변수 사용 불가
  • 목적: Race condition 방지
  • 응용: 반복자 무효화(iterator invalidation?) 방지, 뮤텍스 유출 방지 등
  • 이러한 작업들이 모두 컴파일 타임에 이루어지므로(zero-cost abstraction 등 포함) 성능 손해가 없음

Rust의 장점:

  • C++동등/이상 수준의 zero-cost 추상화
  • GC 없음(모든 메모리 관리는 Compile-time에 처리)
  • Mozilla, Samsung의 지원
  • Race condition, Dangling pointer 등의 메모리 오류를 컴파일 상황에서 방지

Rust의 단점:

  • 모든 메모리 오류를 예방할 수는 없음(특히 Deadlock)
  • 높은 난이도(C++보다는 나은 것으로 알려짐)
  • 아름답지 않은(Not pythonic) 코드(기호로 점철됨)

Go의 동시성 지원: Goroutine이 핵심

  • Erlang에서 영향을 받은 모델
  • Go runtime에서 자체 관리하는 경량 마이크로스레드
  • 6KB 정도의 Stack 메모리만 할당(수천 개를 생성해도 부하 없음)

Go의 특징

  • C-family 문법(struct, 함수 선언 등) + Pythonic한 문법(세미콜론 불필요, 한 번에 여러 변수 할당 등)
  • 덕 타이핑 기반의 OOP(struct에 함수를 붙여서 사용, 상속 X, 인터페이스 기반)
  • Goroutine
  • Channel
  • Unbuffered: 동기, Buffered: 버퍼가 찰 때까지 비동기
  • 자료형에 send 전용/receive 전용 명시 가능
  • First-In-First-Out
  • 명시적 동기화에 이용(chan struct{} 등으로 signal 전달 등)
  • C와의 접착성이 좋음(cgo)
  • 매우 방대한 툴(GoFmt, pprof, heapdump, race detector)
  • 다양한 디버깅 툴(pprof, heapdump, tracer, race detector)이 기본 제공되나 디버깅이 어렵다는 평가가 있음(글쎄?)
  • GC 기반(Go 1.5부터 성능 개선, 최악 지연 시간이 <10ms)
  • 매우 빠른 컴파일 속도

Go의 동시성 모델

  • 고전적 방법(Mutex): sync.Mutex, sync.RWMutex 사용
  • 장점: 확실한 상호 배제로 Race condition 방지
  • 단점: Deadlock의 위험(두 번 Lock하기, 서로 맞물리는 Lock 의존성) (Dining philosophers 문제)
  • Gophers' method(Go Concurrency Patterns, Advanced Go Concurrency Patterns, http://blog.golang.org/context, http://blog.golang.org/pipelines): Channel을 이용한 명시적 동기화
  • 모든 Goroutine은 자기에게 할당된 변수만 접근, 상대 간섭은 Channel을 이용
  • 다른 Goroutine에 접근하려면 클로저(func() {})를 Channel로 전달
  • sync.WaitGroup 또는 chan struct{}를 활용해 다른 Goroutine들과 동기화
  • 장점: Mutex를 사용하지 않으므로 Mutex에 의한 Deadlock 위험이 없다. 제어 흐름을 간단히 표현 가능하다
  • 단점: 채널 남발 시 비동기 전송으로 인해 역시 Deadlock 가능성이 있음

Synopsis 언어의 목표:

  • Rust의 난해함을 해결(더 쉬운 언어)
  • Rust보다 간결한 문법(Go-Style?)
  • Go/Rust에서 해결하지 못한 Deadlock 이슈 해결
  • Rust의 Ownership 모델 차용?(Race 이슈 해결)
  • Rust의 block is an expression 차용
  • 속도/안정성 보다는 Deadlock 해결을 위한 Concept 제시가 주된 목적

Synopsis Concept

  • No Mutex(컴파일타임 추론)
  • No Class(Go와 같은 구조체에 대한 함수 정리만을 지원)
  • Buzzer(Go의 channel broadcasting과 유사)
  • RunForm
  • 변수 Import/Export
  • Block 스코프
  • Move by default(bool, int, uint, float 제외)
  • 정적 타입

RunForm on Synopsis

runform R1 { // async runform 선언도 가능
    int A = 9 // 기본값
    int B = 7
    int NeedDef // 선언 필요
    // RunForm Field의 lifetime은 RunForm 내에서 계속 유지
} Form1 (arg1, arg2) { // arg1, arg2의 lifetime은 Form1 내부에서만 유지
    // Do something
    arg2 // arg2의 lifetime을 다음 Form으로 연장
} Form2 (arg1 = 3) { // arg1 기본값 설정
    // Do something
} { // Anonymous Form(Form2 후 바로 실행, 연장하지 않은 Form2의 모든 lifetime 소멸)
    // Do something
} Form3 { // 인자 없을 시 () 생략가능
    // Do something
    go Form1(1, 2)// Form1로 점프
    // 따로 인자가 필요 없는 Form2는 go Form2와 같이 () 생략 가능
}


R1{NeedDef:3}(2,3)(3)() // R1 생성 및 R3까지 바로 실행
var a R1
a = R1{NeedDef: 3}(2, 3) // R1 생성 후 Form1까지 실행
if stat a == R1.Form1 { // Form1 구동이 끝났으므로 Form1 상태. 조건문은 참
    // 이 코드는 실행됩니다
}
// Async RunForm의 경우 wait문을 이용해 Form의 구동이 끝날 때까지 대기 가능
// 만약 Sleep된 Form 이외의 모든 Form이 인자 명시가 필요 없는 상황이라면, ... 으로 모두 실행 가능(form batching)
// ex) R1{NeedDef: 3}(2,3)...

Variable export/import, mutex 관리, drop statement

async runform R2 {
    int A = 3 // 다른 Thread에서 A에 접근 시 Mutex를 잠근다(Lock).
} Form1 {
    var aa int
    aa = 3
    export aa // aa는 Form1 Sleep 까지 Lock 상태이다.
} { // 이 시점에서 잠시 Form1은 Sleep 상태가 되며 A, aa의 Lock이 해제된다. aa의 lifetime은 소멸되지 않는다.
    // Do something with aa
    drop aa // aa는 이제 이 RunForm 내에서 필요하지 않음을 선언한다. aa의 Lock이 다시 해제된다. 이 아래 코드 및 Form에서는 aa를 사용할 수 없다. 다시 사용하려면 import block을 사용한다.
}
// 주의: Re-export로 중복 Mutex 생성을 방지하기 위해, export가 들어간 Form에는 goto 사용이 제한된다.

var a R2
a = R2{}

import a.aa { // 여러 개인 경우 import (aa, bb) {}, aa가 아직 export되지 않았으면 export될 때까지 대기한다.
    // aa는 이제 Lock되었다.
    // Do something with aa
} // aa의 Lock은 다시 해제된다
// Block은 Expression으로 작용하므로 Rust와 같이 값을 돌려줄 수도 있다.

RunGroup

var g1 rungroup(R2) // R2는 무조건 Async이여야 한다.
g1.New(R2{})(2)... // .New() 이후는 기존 RunForm 실행과 동일

각 RunGroup은 하나의 Thread를 할당받고, 이 RunGroup 내에서 Async RunForm들이 구동된다. Form Sleep 상태에서는 다른 RunForm으로 실행 상태가 yield될 수 있다.


RunForm의 목적

  • 프로그래머가 .Lock(), .Unlock()을 사용하지 않고도 Multi-Thread 상황에서 변수 접근을 처리하는 방법을 제공
  • Form Sleep 상태에서 자동적으로 Unlock되고, Form 내부에서는 drop하지 않는 이상 계속 Lock을 가지고 있음
  • 변수의 Lock을 release하기 위해서 짧은 Anonymous Form들을 이어붙여 자주 Sleep시키는 것을 장려
  • function, class, struct의 융합
  • function은 Field가 없는 RunForm, struct는 Form이 없는 RunForm이 됨

RunGroup의 목적

  • 프로그래머가 비슷한/동일한 작업을 묶어서 비동기로 처리해야 하는 경우(세션 처리, 여러 DB로 쿼리 등)를 만났을 때, RunGroup 사용을 장려
  • 각 RunGroup은 하나씩 Thread를 받고, 각 Form은 Sleep 전까지는 yield되지 않으므로 여러 RunForm 간의 흐름을 보장 가능
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment