웹 락스 API

W3C 최초 공개 워킹 드래프트,

이 문서에 대한 자세한 내용
이 버전:
https://www.w3.org/TR/2023/WD-web-locks-20230105/
최신 공개 버전:
https://www.w3.org/TR/web-locks/
편집자 초안:
https://w3c.github.io/web-locks/
히스토리:
https://www.w3.org/standards/history/web-locks
테스트 스위트:
https://github.com/web-platform-tests/wpt/tree/master/web-locks
피드백:
GitHub
명세 내 인라인
편집자:
(Google Inc.)
(Mozilla)

요약

이 문서는 스크립트가 리소스에 대해 비동기적으로 락을 획득하고, 작업이 수행되는 동안 락을 유지하며, 작업이 끝나면 락을 해제할 수 있는 웹 플랫폼 API를 정의합니다. 락이 유지되는 동안 동일한 출처(origin)의 다른 스크립트는 동일한 리소스에 대해 락을 획득할 수 없습니다. 이를 통해 웹 애플리케이션 내의 컨텍스트(윈도우, 워커 등)가 리소스 사용을 조율할 수 있습니다.

이 문서의 상태

이 섹션은 이 문서가 발행된 시점의 상태를 설명합니다. 현재 W3C 발행 목록과 이 기술 보고서의 최신 개정본은 W3C 기술 보고서 색인 https://www.w3.org/TR/ 에서 확인할 수 있습니다.

이 문서는 웹 애플리케이션 워킹 그룹에서 워킹 드래프트로 발행되었습니다. 이 문서는 W3C 권고안이 될 예정입니다.

이 문서는 웹 애플리케이션 워킹 그룹에서 권고안 트랙을 따라 최초 공개 워킹 드래프트로 발행되었습니다. 이 명세에 대한 피드백과 의견을 환영합니다. GitHub 이슈를 이용해 주세요. 과거 논의는 public-webapps@w3.org 아카이브에서 확인할 수 있습니다.

최초 공개 워킹 드래프트로 발행되었다고 해서 W3C 및 회원사의 보증을 의미하지는 않습니다. 이 문서는 초안이며 언제든 다른 문서로 갱신, 대체 또는 폐지될 수 있습니다. 진행 중인 작업 외에 이 문서를 인용하는 것은 부적절합니다.

이 문서는 W3C 특허 정책에 따라 운영되는 그룹에서 제작되었습니다. W3C는 해당 그룹 산출물과 관련되어 공개된 특허 공개 목록을 관리합니다. 해당 페이지에는 특허 공개 방법도 안내되어 있습니다. 특허의 필수 클레임(Claim)을 실제로 알고 있는 자는 W3C 특허 정책 6절에 따라 정보를 공개해야 합니다.

이 문서는 2021년 11월 2일 W3C 프로세스 문서의 적용을 받습니다.

1. 소개

이 섹션은 비규범적입니다.

락 요청은 스크립트가 특정 리소스 이름모드에 대해 수행합니다. 스케줄링 알고리즘은 현재 및 이전 요청 상태를 살펴보고, 결국 락 요청을 승인합니다. 은 승인된 요청이며 리소스 이름모드를 가집니다. 이는 스크립트에 반환되는 객체로 표현됩니다. 락이 유지되는 동안(이름과 모드에 따라) 다른 락 요청의 승인을 막을 수 있습니다. 스크립트가 락을 해제하면, 그 시점에 다른 락 요청이 승인될 수 있습니다.

API는 필요에 따라 사용할 수 있는 선택적 기능을 제공합니다. 예를 들면:

협력적인 조율은 agent가 동일한 storage bucket을 공유하는 범위 내에서 이뤄지며, 이는 여러 agent cluster에 걸칠 수도 있습니다.

1.1. 사용 개요

API는 다음과 같이 사용합니다:

  1. 락을 요청한다.

  2. 비동기 작업에서 락을 보유한 상태로 작업을 수행한다.

  3. 작업이 완료되면 락이 자동으로 해제된다.

1.2. 동기 부여 사례

웹 기반 문서 편집기는 빠른 접근을 위해 메모리에 상태를 저장하고, 복원력 및 오프라인 사용을 위해 Indexed Database API와 같은 스토리지 API에 변경 사항(레코드 시리즈)을 영속화하며, 서버에도 동기화하여 여러 기기에서 사용할 수 있도록 합니다. 동일한 문서를 두 개의 탭에서 동시에 열 경우, 작업은 탭 간에 조율되어야 하며, 한 번에 한 탭만 변경 또는 동기화를 할 수 있어야 합니다. 이를 위해 탭들은 어떤 탭이 실제로 변경(및 스토리지 API와 메모리 상태를 동기화)할지를 조율하고, 활성 탭이 이동, 종료, 크래시될 때 다른 탭이 활성화될 수 있도록 해야 합니다.

데이터 동기화 서비스에서는 "주 탭"이 지정됩니다. 이 탭만 특정 작업(예: 네트워크 동기화, 대기 중인 데이터 정리 등)을 수행해야 하며, 락을 획득한 뒤 해제하지 않습니다. 다른 탭들은 락을 획득하려고 시도하며, 이러한 시도는 큐에 쌓입니다. "주 탭"이 크래시되거나 닫히면, 다른 탭 중 하나가 락을 획득해 새로운 주 탭이 됩니다.

Indexed Database API는 하나의 출처(origin) 내에서 여러 이름의 스토리지 파티션에 대해 공유 읽기 및 단독 쓰기 접근을 허용하는 트랜잭션 모델을 정의합니다. 이 개념을 프리미티브로 노출하면, 어떤 웹 플랫폼 활동도 리소스 가용성에 따라 스케줄링할 수 있습니다. 예를 들어, 다른 스토리지 타입(캐시 [Service-Workers] 등)이나 스토리지 타입 간, 심지어 비스토리지 API(예: 네트워크 fetch)까지도 트랜잭션을 조합할 수 있게 됩니다.

2. 개념

이 명세의 목적상:

사용자 에이전트락 태스크 큐를 가지며, 이는 새 병렬 큐를 시작한 결과입니다.

태스크 소스는 아래에서 큐잉된 단계웹 락 태스크 소스입니다.

2.1. 리소스 이름

리소스 이름은 웹 애플리케이션이 추상적 리소스를 나타내기 위해 선택한 JavaScript 문자열입니다.

리소스 이름은 스케줄링 알고리즘 외에는 외부 의미가 없지만, agent가 동일한 스토리지 버킷을 공유하는 범위에서는 전역적입니다. 웹 애플리케이션은 어떤 리소스 이름 지정 방식도 자유롭게 사용할 수 있습니다.

U+002D 하이픈(-)으로 시작하는 리소스 이름은 예약되어 있습니다. 이를 요청하면 예외가 발생합니다.

2.2. 락 관리자

락 관리자락 요청의 상태를 캡슐화합니다. 각 스토리지 버킷은 Web Locks API를 위한 연관 스토리지 병을 통해 하나의 락 관리자를 포함합니다.

참고: 동일한 사용자 에이전트 내에서 동일한 스토리지 버킷을 공유하는 페이지와 워커(agent들)는 서로 관련 없는 브라우징 컨텍스트에 있더라도 락 관리자를 공유합니다.

락 관리자를 획득하려면, 환경 설정 객체 environment가 주어졌을 때 다음 단계를 수행합니다:
  1. map로컬 스토리지 병 맵 획득의 결과로 한다. 인자로 environment와 "web-locks"를 전달한다.

  2. map이 실패라면, 실패를 반환한다.

  3. bottlemap의 연관 스토리지 병으로 한다.

  4. bottle의 연관 락 관리자를 반환한다.

여기서 [Storage]와의 통합을 구체화해야 하며, 환경에서 락 관리자를 올바르게 획득하는 방법도 포함해야 합니다.

2.3. 모드와 스케줄링

모드는 "exclusive" 또는 "shared" 중 하나입니다. 모드는 일반적인 읽기-쓰기 락 패턴을 모델링할 수 있습니다. "exclusive" 락이 유지되는 동안 해당 이름의 다른 락은 승인되지 않습니다. "shared" 락이 유지되는 동안에는 같은 이름의 다른 "shared" 락이 승인될 수 있지만, "exclusive" 락은 승인되지 않습니다. API의 기본 모드는 "exclusive"입니다.

추가 속성(예: 타임아웃, 공정성 등)이 스케줄링에 영향을 줄 수 있습니다.

2.4.

은 공유 리소스에 대한 독점적 접근을 나타냅니다.

agent를 가지며, 이는 agent입니다.

clientId를 가지며, 이는 불투명한 문자열입니다.

manager를 가지며, 이는 락 관리자입니다.

name을 가지며, 이는 리소스 이름입니다.

mode를 가지며, "exclusive" 또는 "shared" 중 하나입니다.

waiting promise를 가지며, 이는 Promise입니다.

released promise를 가지며, 이는 Promise입니다.

락 관리자보유 락 집합을 가지며, 이는 집합 형태의 입니다.

lockwaiting promise가 settle(fulfill 또는 reject)될 때, 다음 단계를 락 태스크 큐에 큐잉한다:

  1. lock을 해제한다.

  2. resolve lockreleased promiselockwaiting promise로 한다.

2.5. 락 요청

락 요청에 대한 대기 중인 요청을 나타냅니다.

락 요청은 다음 구조체항목으로 구성됩니다: agent, clientId, manager, name, mode, callback, promise, 그리고 signal입니다.

락 요청 큐 형태의 락 요청입니다.

락 관리자락 요청 큐 맵을 가지며, 이는 형태로 리소스 이름에서 락 요청 큐로 연결됩니다.

락 요청 큐 가져오기락 요청 큐 맵 queueMap에서 리소스 이름 name을 인자로 다음 단계를 수행합니다:

  1. 만약 queueMap[name]이 존재하지 않는다면, queueMap[name]을 새로운 빈 lock request queue로 설정한다.

  2. queueMap[name]을 반환한다.

락 요청 requestgrantable(승인 가능)인지 여부는 아래 절차가 true를 반환하는지로 판단합니다:

  1. managerrequestmanager로 한다.

  2. queueMapmanager락 요청 큐 맵으로 한다.

  3. namerequestname으로 한다.

  4. queue락 요청 큐 가져오기의 결과로 queueMapname을 사용해 얻는다.

  5. heldmanager보유 락 집합으로 한다.

  6. moderequestmode로 한다.

  7. queue비어있지 않고 requestqueue의 첫 항목이 아니면 false를 반환한다.

  8. mode가 "exclusive"이면, held 내에 namename과 같은 것이 없으면 true를, 있으면 false를 반환한다.

  9. 그 외의 경우, mode가 "shared"라면, held 내에 mode가 "exclusive"이고 namename과 같은 것이 없으면 true, 있으면 false를 반환한다.

2.6. 락 종료

문서 언로드 정리 단계document에 대해 실행될 때, 해당 남은 락과 요청 종료를 그 agent로 수행합니다.

agent가 종료되면, 남은 락과 요청 종료를 해당 agent로 수행합니다.

현재는 워커에만 적용되며, 워커 종료 시 단계를 실행하는 규범적인 방법이 없어 정의가 모호합니다.

남은 락과 요청 종료agent를 인자로 다음 단계를 락 태스크 큐에 큐잉합니다:

  1. 락 요청 requestagentagent와 같은 각 요청에 대해:

    1. 요청 중단 request.

  2. lockagentagent와 같은 각 락에 대해:

    1. 락 해제 lock.

3. API

[SecureContext]
interface mixin NavigatorLocks {
  readonly attribute LockManager locks;
};
Navigator includes NavigatorLocks;
WorkerNavigator includes NavigatorLocks;

환경 설정 객체LockManager 객체를 가집니다.

Navigator/locks

모든 현행 엔진에서 지원됩니다.

Firefox96+Safari15.4+Chrome69+
Opera?Edge79+
Edge (Legacy)?IE없음
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

locks getter의 단계는 this관련 설정 객체LockManager 객체를 반환하는 것입니다.

3.2. LockManager 클래스

LockManager

모든 현행 엔진에서 지원됩니다.

Firefox96+Safari15.4+Chrome69+
Opera?Edge79+
Edge (Legacy)?IE없음
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
[SecureContext, Exposed=(Window,Worker)]
interface LockManager {
  Promise<any> request(DOMString name,
                       LockGrantedCallback callback);
  Promise<any> request(DOMString name,
                       LockOptions options,
                       LockGrantedCallback callback);

  Promise<LockManagerSnapshot> query();
};

callback LockGrantedCallback = Promise<any> (Lock? lock);

enum LockMode { "shared", "exclusive" };

dictionary LockOptions {
  LockMode mode = "exclusive";
  boolean ifAvailable = false;
  boolean steal = false;
  AbortSignal signal;
};

dictionary LockManagerSnapshot {
  sequence<LockInfo> held;
  sequence<LockInfo> pending;
};

dictionary LockInfo {
  DOMString name;
  LockMode mode;
  DOMString clientId;
};

LockManager 인스턴스는 스크립트가 락 요청을 하고 락 관리자의 상태를 질의할 수 있도록 합니다.

3.2.1. request() 메서드

LockManager/request

모든 현행 엔진에서 지원됩니다.

Firefox96+Safari15.4+Chrome69+
Opera?Edge79+
Edge (Legacy)?IE없음
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
promise = navigator . locks . request(name, callback)
promise = navigator . locks . request(name, options, callback)

request() 메서드는 락을 요청하기 위해 호출됩니다.

name(첫 번째 인자)은 리소스 이름 문자열입니다.

callback(마지막 인자)은 콜백 함수로, 락이 승인될 때 Lock과 함께 호출됩니다. 이는 스크립트에서 지정하며 일반적으로 async 함수입니다. 락은 콜백 함수가 완료될 때까지 유지됩니다. 비동기 함수가 아닌 콜백이 전달되면, 즉시 resolve되는 promise로 자동 래핑되므로 락은 동기 콜백 실행 기간만 유지됩니다.

반환되는 promise는 락이 해제된 뒤 콜백의 결과값으로 resolve(또는 reject)되거나, 요청이 중단된 경우 reject됩니다.

예시:

try {
  const result = await navigator.locks.request('resource', async lock => {
    // 여기서 락이 유지됩니다.
    await do_something();
    await do_something_else();
    return "ok";
    // 이제 락이 해제됩니다.
  });
  // |result|는 콜백의 반환값입니다.
} catch (ex) {
  // 콜백에서 throw가 발생하면 여기서 잡힙니다.
}

콜백이 어떤 이유로든 종료되면 락도 해제됩니다. 즉, 코드가 반환되거나 throw될 때 모두 락이 풀립니다.

options 딕셔너리를 두 번째 인자로 지정할 수 있습니다. callback 인자는 항상 마지막입니다.

options . mode

mode 옵션은 "exclusive" (명시하지 않으면 기본값) 또는 "shared"일 수 있습니다. 여러 탭/워커가 동일 리소스에 대해 "shared" 모드로 락을 동시에 가질 수 있지만, "exclusive" 모드에서는 단 하나의 탭/워커만 해당 리소스 락을 가질 수 있습니다.

가장 흔한 용도는 여러 reader가 동시에 리소스에 접근할 수 있도록 하되, 변경을 막는 것입니다. reader 락이 모두 해제된 뒤에는 단독 writer가 락을 획득해 변경을 수행할 수 있고, 그 다음에는 또 다른 writer 또는 여러 shared reader가 락을 획득할 수 있습니다.

await navigator.locks.request('resource', {mode: 'shared'}, async lock => {
  // 여기서 락이 유지됩니다. 다른 컨텍스트도 shared 모드로 락을 가질 수 있지만,
  // exclusive 모드로는 누구도 락을 가질 수 없습니다.
});
options . ifAvailable

ifAvailable 옵션이 true면, 락이 추가 대기 없이 바로 승인될 수 있을 때만 승인됩니다. 단, 이것이 동기적이라는 의미는 아니며, 많은 사용자 에이전트에서 락 승인 가능 여부를 확인하려면 프로세스 간 통신이 필요할 수도 있습니다. 락을 승인할 수 없으면 콜백이 null로 호출됩니다. (이는 예상된 동작이므로 request가 reject되지 않습니다.)

await navigator.locks.request('resource', {ifAvailable: true}, async lock => {
  if (!lock) {
    // 획득하지 못함. 적절한 처리를 할 수 있습니다.
    return;
  }
  // 여기서 락이 유지됩니다.
});
options . signal

signal 옵션에는 AbortSignal을 지정할 수 있습니다. 이를 이용하면 락 요청이 적시에 승인되지 않을 경우 중단할 수 있습니다:

const controller = new AbortController();
setTimeout(() => controller.abort(), 200); // 최대 200ms 대기.

try {
  await navigator.locks.request(
    'resource', {signal: controller.signal}, async lock => {
      // 여기서 락이 유지됩니다.
  });
  // 여기서 락 처리가 끝납니다.
} catch (ex) {
  // |ex|는 타이머가 실행되면 "AbortError" 이름의 DOMException입니다.
}

락이 승인되기 전에 abort가 발생하면, 요청 promise는 AbortError로 reject됩니다. 락이 승인된 뒤에는 signal은 무시됩니다.

options . steal

steal 옵션이 true면, 해당 리소스의 기존 락들은 모두 해제되고(이때 해당 락의 released promiseAbortError로 resolve됨), 요청이 승인되어 기존 큐에 있던 요청들을 선점합니다.

웹 애플리케이션이 복구 불가능한 상태를 감지한 경우(예: Service Worker가 락을 보유한 탭이 더 이상 응답하지 않음을 판단한 경우), 이 옵션으로 락을 "steal"할 수 있습니다.

request(name, callback)request(name, options, callback) 메서드의 단계:

  1. options가 전달되지 않았다면, options를 기본 멤버가 채워진 새로운 LockOptions 딕셔너리로 한다.

  2. environmentthis관련 설정 객체로 한다.

  3. environment관련 글로벌 객체연관 Document완전히 활성화된 상태가 아니면 InvalidStateError DOMException으로 reject되는 promise를 반환한다.

  4. manager락 관리자 획득의 결과로 environment를 전달하여 얻는다. 실패를 반환했다면, SecurityError DOMException으로 reject되는 promise를 반환한다.

  5. name이 U+002D 하이픈(-)으로 시작하면 NotSupportedError DOMException으로 reject되는 promise를 반환한다.

  6. options["steal"]와 options["ifAvailable"]가 모두 true면, NotSupportedError DOMException으로 reject되는 promise를 반환한다.

  7. options["steal"]가 true이고 options["mode"] 가 "exclusive"가 아니면, NotSupportedError DOMException으로 reject되는 promise를 반환한다.

  8. options["signal"]가 존재하고, options["steal"] 또는 options["ifAvailable"]가 true라면, NotSupportedError DOMException으로 reject되는 promise를 반환한다.

  9. options["signal"]가 존재하고 이미 aborted 상태라면, abort reason으로 reject되는 promise를 반환한다.

  10. promise새 promise로 한다.

  11. 락 요청promise, 현재 agent, environmentid, manager, callback, name, options["mode"], options["ifAvailable"], options["steal"], options["signal"]로 실행한다.

  12. promise를 반환한다.

3.2.2. query() 메서드

LockManager/query

모든 현행 엔진에서 지원됩니다.

Firefox96+Safari15.4+Chrome69+
Opera?Edge79+
Edge (Legacy)?IE없음
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
state = await navigator . locks . query()

query() 메서드는 특정 출처(origin)의 락 관리자 상태 스냅샷을 생성할 수 있어, 웹 애플리케이션에서 락 사용 현황을 로깅 또는 디버깅 목적으로 확인할 수 있습니다.

반환되는 promise는 아래 형태의 state(JSON과 유사한 평범한 데이터 구조)로 resolve됩니다:

{
  held: [
    { name: "resource1", mode: "exclusive",
      clientId: "8b1e730c-7405-47db-9265-6ee7c73ac153" },
    { name: "resource2", mode: "shared",
      clientId: "8b1e730c-7405-47db-9265-6ee7c73ac153" },
    { name: "resource2", mode: "shared",
      clientId: "fad203a5-1f31-472b-a7f7-a3236a1f6d3b" },
  ],
  pending: [
    { name: "resource1", mode: "exclusive",
      clientId: "fad203a5-1f31-472b-a7f7-a3236a1f6d3b" },
    { name: "resource1", mode: "exclusive",
      clientId: "d341a5d0-1d8d-4224-be10-704d1ef92a15" },
  ]
}

clientId 필드는 고유한 컨텍스트(프레임 또는 워커)에 해당하며, Clientid 속성 값과 동일합니다.

query() 메서드의 단계:

  1. environmentthis관련 설정 객체로 한다.

  2. environment관련 글로벌 객체연관 Document완전히 활성화 상태가 아니면 InvalidStateError DOMException으로 reject되는 promise를 반환한다.

  3. manager락 관리자 획득의 결과로 environment를 전달하여 얻는다. 실패라면 SecurityError DOMException으로 reject되는 promise를 반환한다.

  4. promise새 promise로 한다.

  5. 다음 단계 큐잉을 통해 manager에 대해 promise락 상태 스냅샷을 생성한다. 큐는 락 태스크 큐를 이용한다.

  6. promise를 반환한다.

3.3. Lock 클래스

Lock

모든 현행 엔진에서 지원됩니다.

Firefox96+Safari15.4+Chrome69+
Opera?Edge79+
Edge (Legacy)?IE없음
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?
[SecureContext, Exposed=(Window,Worker)]
interface Lock {
  readonly attribute DOMString name;
  readonly attribute LockMode mode;
};

Lock 객체는 연관된 을 가지고 있습니다.

Lock/name

모든 현행 엔진에서 지원됩니다.

Firefox96+Safari15.4+Chrome69+
Opera?Edge79+
Edge (Legacy)?IE없음
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

name getter의 단계는 연관된 name을 반환하는 것입니다.

Lock/mode

모든 현행 엔진에서 지원됩니다.

Firefox96+Safari15.4+Chrome69+
Opera?Edge79+
Edge (Legacy)?IE없음
Firefox for Android?iOS Safari?Chrome for Android?Android WebView?Samsung Internet?Opera Mobile?

mode getter의 단계는 연관된 mode를 반환하는 것입니다.

4. 알고리즘

4.1. 락 요청

락을 요청하려면 promise, agent, clientId, manager, callback, name, mode, ifAvailable, steal, signal을 사용한다:
  1. request를 새로운 락 요청(agent, clientId, manager, name, mode, callback, promise, signal)로 한다.

  2. signal이 존재하면 add 알고리즘 signal to abort the request requestsignal에 추가한다.

  3. 다음 단계를 락 태스크 큐에 큐잉한다:

    1. queueMapmanager락 요청 큐 맵으로 한다.

    2. queue락 요청 큐 가져오기의 결과로 queueMapname을 사용해 얻는다.

    3. heldmanager보유 락 집합으로 한다.

    4. steal이 true이면, 다음 단계를 실행한다:

      1. held의 각 lock에 대해:

        1. locknamename과 같으면 다음 단계를 실행:

          1. held에서 lock을 제거한다.

          2. lockreleased promise를 "AbortError" DOMException으로 reject한다.

      2. requestqueue에 앞에 추가한다.

    5. 그 외의 경우, 다음 단계를 실행한다:

      1. ifAvailable이 true이고 requestgrantable이 아니면, 다음 단계를 callback관련 설정 객체responsible event loop에 큐잉한다:

        1. rcallback을 인자로 null을 전달해 호출한 결과로 한다.

        2. promiser로 resolve하고 이 단계를 중단한다.

      2. queuerequest를 추가한다.

    6. 락 요청 큐 처리queue에 대해 실행한다.

  4. request를 반환한다.

4.2. 락 해제

락 해제 lock:
  1. 이 단계가 락 태스크 큐에서 실행 중임을 단언한다.

  2. managerlockmanager로 한다.

  3. queueMapmanager락 요청 큐 맵으로 한다.

  4. namelock리소스 이름으로 한다.

  5. queue락 요청 큐 가져오기의 결과로 queueMapname을 사용해 얻는다.

  6. manager보유 락 집합에서 lock을 제거한다.

  7. 락 요청 큐 처리queue에 대해 실행한다.

4.3. 요청 중단

요청 중단 request:
  1. 이 단계가 락 태스크 큐에서 실행 중임을 단언한다.

  2. managerrequestmanager로 한다.

  3. namerequestname으로 한다.

  4. queueMapmanager락 요청 큐 맵으로 한다.

  5. queue락 요청 큐 가져오기의 결과로 queueMapname을 사용해 얻는다.

  6. requestqueue에서 제거한다.

  7. 락 요청 큐 처리queue에 대해 실행한다.

signal to abort the request requestsignal:
  1. 다음 단계 큐잉을 통해 요청 중단 request락 태스크 큐에 추가한다.

  2. requestpromisesignalabort reason으로 reject한다.

4.4. 주어진 리소스 이름에 대한 락 요청 큐 처리

락 요청 큐 처리 queue를 실행하려면:
  1. 단언: 이 단계들은 락 태스크 큐에서 실행되고 있다.

  2. requestqueue에서 반복한다:

    1. requestgrantable이 아니면 return한다.

      참고: 큐에서 첫 번째 항목만 grantable이다. 따라서 grantable이 아니면 이후의 모든 항목도 자동으로 grantable이 아니다.

    2. queue에서 request를 제거한다.

    3. agentrequestagent로 한다.

    4. managerrequestmanager로 한다.

    5. clientIdrequestclientId로 한다.

    6. namerequestname으로 한다.

    7. moderequestmode로 한다.

    8. callbackrequestcallback으로 한다.

    9. prequestpromise로 한다.

    10. signalrequestsignal로 한다.

    11. waiting새로운 promise로 한다.

    12. lock을 새로운 으로 하며, agent agent, clientId clientId, manager manager, mode mode, name name, released promise p, waiting promise waiting을 포함한다.

    13. manager보유 락 집합lock을 추가한다.

    14. 다음 단계를 callback관련 설정 객체responsible event loop에 큐잉한다:

      1. signal이 존재하면 다음 단계를 실행한다:

        1. signalaborted 상태면 다음 단계를 실행:

          1. 다음 단계를 락 태스크 큐에 큐잉한다:

            1. 락 해제 lock.

          2. return한다.

        2. signal to abort the request request 알고리즘을 signal에서 제거한다.

      2. rcallback을 인자로, lock에 연관된 새로운 Lock 객체를 전달하여 호출한 결과로 한다.

      3. waitingr로 resolve한다.

4.5. 락 상태 스냅샷

락 상태 스냅샷managerpromise로 실행하려면:
  1. 단언: 이 단계들은 락 태스크 큐에서 실행되고 있다.

  2. pending을 새로운 리스트로 한다.

  3. manager락 요청 큐 맵값들에 대해 각 queue를 반복한다:

    1. requestqueue에서 반복한다:

      1. Append «[ "name" → requestname, "mode" → requestmode, "clientId" → requestclientId ]»를 pending에 추가한다.

  4. held를 새로운 리스트로 한다.

  5. manager보유 락 집합의 각 lock을 반복한다:

    1. Append «[ "name" → lockname, "mode" → lockmode, "clientId" → lockclientId ]»를 held에 추가한다.

  6. promise를 «[ "held" → held, "pending" → pending ]»로 resolve한다.

5. 사용 시 고려 사항

이 섹션은 비규범적입니다.

5.1. 교착 상태

교착 상태는 동시성 컴퓨팅의 개념이며, 특정 락 관리자 범위 내에서 이 API에 의해 발생할 수 있습니다.

교착 상태를 방지하려면 주의가 필요합니다. 한 가지 방법은 여러 락을 항상 엄격한 순서로 획득하는 것입니다.

6. 보안 및 개인정보 보호 고려 사항

6.1. 락 범위

락 관리자의 범위 정의는 개인정보 경계 설정에 중요합니다. 락은 임시 상태 보존 수단으로 사용될 수 있으며, 스토리지 API처럼 통신 메커니즘으로 쓰일 수 있으므로, 저장소와 동등 이상의 권한을 가져서는 안 됩니다. 사용자 에이전트가 어떤 서비스에 더 세분화된 구분을 적용한다면, 다른 서비스에도 동일하게 적용해야 합니다. 예컨대, 개인정보 보호를 위해 사용자 에이전트가 동일 출처 내 탑레벨 페이지(퍼스트파티)와 교차 출처 iframe(서드파티)에 서로 다른 저장소 파티션을 제공한다면, 락 역시 동일하게 파티셔닝해야 합니다.

이는 웹 애플리케이션 개발자에게도 합리적인 기대를 제공합니다. 예를 들어 저장소 리소스에 락을 획득하면, 동일 출처의 모든 브라우징 컨텍스트는 같은 상태를 관찰해야 합니다.

6.2. 프라이빗 브라우징

모든 프라이빗 모드 브라우징 세션은 이 API의 목적상 별도의 사용자 에이전트로 간주됩니다. 즉, 프라이빗 세션 외부에서 요청/보유한 락은 프라이빗 세션 내부의 요청/보유 상태에 영향을 주지 않으며, 그 반대도 마찬가지입니다. 이는 웹사이트가 세션이 "시크릿" 모드인지 확인하지 못하도록 하면서, 세션 간 통신 메커니즘도 허용하지 않습니다.

6.3. 구현 위험

구현체는 락이 서로 다른 출처에 걸치지 않도록 반드시 보장해야 합니다. 그렇지 않으면 서로 다른 출처의 스크립트 간 통신을 위한 사이드 채널이 생길 수 있고, 한 출처의 스크립트가 다른 출처의 동작을 방해할 수 있습니다(예: 서비스 거부).

6.4. 체크리스트

W3C TAG는 명세 편집자를 위한 보안 및 개인정보 자가진단 문서를 개발했습니다. 주요 질문을 다시 검토하면:

7. 감사의 글

다음 분들께 깊은 감사를 드립니다: Alex Russell, Andreas Butler, Anne van Kesteren, Boris Zbarsky, Chris Messina, Darin Fisher, Domenic Denicola, Gus Caplan, Harald Alvestrand, Jake Archibald, Kagami Sascha Rosylight, L. David Baron, Luciano Pacheco, Marcos Caceres, Ralph Chelala, Raymond Toy, Ryan Fioravanti, 그리고 Victor Costan 이 제안서 작성에 도움을 주셨습니다.

Bikeshed 명세 저작 도구를 만들고 유지해준 Tab Atkins, Jr.에게도 특별히 감사드리며, 그의 저작 관련 조언에도 감사드립니다.

적합성

문서 규칙

적합성 요구사항은 설명적 단언과 RFC 2119 용어의 조합으로 표현됩니다. 규범적 부분에서 “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, “OPTIONAL”과 같은 주요 단어는 RFC 2119에 정의된 대로 해석되어야 합니다. 그러나 가독성을 위해 이 명세서에서는 이러한 단어를 모두 대문자로 표기하지 않습니다.

이 명세서의 모든 텍스트는 규범적이며, 명시적으로 비규범적임을 표시한 섹션, 예시, 참고 사항만 예외입니다. [RFC2119]

이 명세서의 예시는 항상 “예를 들어”라는 말로 시작하거나, class="example"와 같이 규범적 텍스트와 구분되어 표시됩니다:

이것은 안내용 예시입니다.

안내용 참고 사항은 “참고”라는 단어로 시작하며, class="note"로 규범적 텍스트와 구분되어 표시됩니다:

참고, 이것은 안내용 참고 사항입니다.

적합 알고리즘

알고리즘 내에서 명령형으로 표현된 요구사항(예: "앞의 공백 문자를 모두 제거한다" 또는 "false를 반환하고 이 단계를 중단한다")는 알고리즘을 소개하는 데 사용된 키워드("must", "should", "may" 등)의 의미에 따라 해석되어야 합니다.

알고리즘이나 특정 단계로 작성된 적합성 요구사항은 최종 결과가 동일하다면 어떤 방식으로든 구현할 수 있습니다. 특히, 이 명세서에서 정의된 알고리즘은 이해하기 쉽도록 작성되었으며, 성능을 위한 것이 아닙니다. 구현자는 최적화를 권장합니다.

색인

이 명세에서 정의된 용어

참조로 정의된 용어

참고 문헌

규범적 참고 문헌

[CSS21]
Bert Bos; 외. Cascading Style Sheets Level 2 Revision 1 (CSS 2.1) Specification. 2011년 6월 7일. REC. URL: https://www.w3.org/TR/CSS21/
[DOM]
Anne van Kesteren. DOM Standard. 현행 표준. URL: https://dom.spec.whatwg.org/
[HTML]
Anne van Kesteren; 외. HTML Standard. 현행 표준. URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. 현행 표준. URL: https://infra.spec.whatwg.org/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. 1997년 3월. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[Storage]
Anne van Kesteren. Storage Standard. 현행 표준. URL: https://storage.spec.whatwg.org/
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. 현행 표준. URL: https://webidl.spec.whatwg.org/

참고용 참고 문헌

[IndexedDB-2]
Ali Alabbas; Joshua Bell. Indexed Database API 2.0. 2018년 1월 30일. REC. URL: https://www.w3.org/TR/IndexedDB-2/
[Service-Workers]
Jake Archibald; Marijn Kruisselbrink. Service Workers. 2022년 7월 12일. CR. URL: https://www.w3.org/TR/service-workers/

IDL 색인

[SecureContext]
interface mixin NavigatorLocks {
  readonly attribute LockManager locks;
};
Navigator includes NavigatorLocks;
WorkerNavigator includes NavigatorLocks;

[SecureContext, Exposed=(Window,Worker)]
interface LockManager {
  Promise<any> request(DOMString name,
                       LockGrantedCallback callback);
  Promise<any> request(DOMString name,
                       LockOptions options,
                       LockGrantedCallback callback);

  Promise<LockManagerSnapshot> query();
};

callback LockGrantedCallback = Promise<any> (Lock? lock);

enum LockMode { "shared", "exclusive" };

dictionary LockOptions {
  LockMode mode = "exclusive";
  boolean ifAvailable = false;
  boolean steal = false;
  AbortSignal signal;
};

dictionary LockManagerSnapshot {
  sequence<LockInfo> held;
  sequence<LockInfo> pending;
};

dictionary LockInfo {
  DOMString name;
  LockMode mode;
  DOMString clientId;
};

[SecureContext, Exposed=(Window,Worker)]
interface Lock {
  readonly attribute DOMString name;
  readonly attribute LockMode mode;
};

이슈 색인

여기에서 [Storage]와의 통합을 구체화하고, 주어진 환경에서 락 관리자를 올바르게 획득하는 방법을 명확히 해야 합니다.
현재는 워커에만 적용되며, 워커 종료 시 단계를 실행하는 규범적인 방법이 없어 정의가 모호합니다.