storageIpc.ts 5.3 KB
Newer Older
B
wip  
Benjamin Pasero 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import { IChannel, IServerChannel } from 'vs/base/parts/ipc/node/ipc';
J
Joao Moreno 已提交
7
import { Event, Emitter } from 'vs/base/common/event';
8 9
import { StorageMainService, IStorageChangeEvent } from 'vs/platform/storage/node/storageMainService';
import { IUpdateRequest, IStorageDatabase, IStorageItemsChangeEvent } from 'vs/base/node/storage';
10
import { mapToSerializable, serializableToMap, values } from 'vs/base/common/map';
11
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
12

13 14 15 16 17 18 19 20 21 22 23
type Key = string;
type Value = string;
type Item = [Key, Value];

interface ISerializableUpdateRequest {
	insert?: Item[];
	delete?: Key[];
}

interface ISerializableItemsChangeEvent {
	items: Item[];
24
}
B
wip  
Benjamin Pasero 已提交
25

26
export class GlobalStorageDatabaseChannel extends Disposable implements IServerChannel {
B
wip  
Benjamin Pasero 已提交
27

28 29 30 31
	private static STORAGE_CHANGE_DEBOUNCE_TIME = 100;

	private _onDidChangeItems: Emitter<ISerializableItemsChangeEvent> = this._register(new Emitter<ISerializableItemsChangeEvent>());
	get onDidChangeItems(): Event<ISerializableItemsChangeEvent> { return this._onDidChangeItems.event; }
B
wip  
Benjamin Pasero 已提交
32

33
	constructor(private storageMainService: StorageMainService) {
34 35 36 37 38 39 40 41 42
		super();

		this.registerListeners();
	}

	private registerListeners(): void {

		// Listen for changes in global storage to send to listeners
		// that are listening. Use a debouncer to reduce IPC traffic.
J
Joao Moreno 已提交
43
		this._register(Event.debounce(this.storageMainService.onDidChangeStorage, (prev: IStorageChangeEvent[], cur: IStorageChangeEvent) => {
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
			if (!prev) {
				prev = [cur];
			} else {
				prev.push(cur);
			}

			return prev;
		}, GlobalStorageDatabaseChannel.STORAGE_CHANGE_DEBOUNCE_TIME)(events => {
			if (events.length) {
				this._onDidChangeItems.fire(this.serializeEvents(events));
			}
		}));
	}

	private serializeEvents(events: IStorageChangeEvent[]): ISerializableItemsChangeEvent {
		const items = new Map<Key, Value>();
B
Benjamin Pasero 已提交
60
		events.forEach(event => items.set(event.key, this.storageMainService.get(event.key)));
61 62

		return { items: mapToSerializable(items) } as ISerializableItemsChangeEvent;
B
wip  
Benjamin Pasero 已提交
63 64 65
	}

	listen(_, event: string): Event<any> {
66 67 68
		switch (event) {
			case 'onDidChangeItems': return this.onDidChangeItems;
		}
B
wip  
Benjamin Pasero 已提交
69 70 71 72

		throw new Error(`Event not found: ${event}`);
	}

J
Johannes Rieken 已提交
73
	call(_, command: string, arg?: any): Promise<any> {
B
wip  
Benjamin Pasero 已提交
74 75
		switch (command) {
			case 'getItems': {
76
				return Promise.resolve(mapToSerializable(this.storageMainService.items));
B
wip  
Benjamin Pasero 已提交
77 78 79
			}

			case 'updateItems': {
80 81 82 83 84
				const items = arg as ISerializableUpdateRequest;
				if (items.insert) {
					for (const [key, value] of items.insert) {
						this.storageMainService.store(key, value);
					}
B
wip  
Benjamin Pasero 已提交
85 86
				}

87 88
				if (items.delete) {
					items.delete.forEach(key => this.storageMainService.remove(key));
B
wip  
Benjamin Pasero 已提交
89 90 91 92 93 94
				}

				return Promise.resolve(); // do not wait for modifications to complete
			}

			case 'checkIntegrity': {
95
				return this.storageMainService.checkIntegrity(arg);
B
wip  
Benjamin Pasero 已提交
96 97 98 99 100 101 102
			}
		}

		throw new Error(`Call not found: ${command}`);
	}
}

103
export class GlobalStorageDatabaseChannelClient extends Disposable implements IStorageDatabase {
B
wip  
Benjamin Pasero 已提交
104 105 106

	_serviceBrand: any;

107 108 109
	private _onDidChangeItemsExternal: Emitter<IStorageItemsChangeEvent> = this._register(new Emitter<IStorageItemsChangeEvent>());
	get onDidChangeItemsExternal(): Event<IStorageItemsChangeEvent> { return this._onDidChangeItemsExternal.event; }

110 111
	private onDidChangeItemsOnMainListener: IDisposable;

112 113
	constructor(private channel: IChannel) {
		super();
B
wip  
Benjamin Pasero 已提交
114

115 116 117 118
		this.registerListeners();
	}

	private registerListeners(): void {
119
		this.onDidChangeItemsOnMainListener = this.channel.listen('onDidChangeItems')((e: ISerializableItemsChangeEvent) => this.onDidChangeItemsOnMain(e));
120 121 122 123 124 125 126
	}

	private onDidChangeItemsOnMain(e: ISerializableItemsChangeEvent): void {
		if (Array.isArray(e.items)) {
			this._onDidChangeItemsExternal.fire({ items: serializableToMap(e.items) });
		}
	}
B
wip  
Benjamin Pasero 已提交
127

J
Johannes Rieken 已提交
128
	getItems(): Promise<Map<string, string>> {
129
		return this.channel.call('getItems').then((data: Item[]) => serializableToMap(data));
B
wip  
Benjamin Pasero 已提交
130 131
	}

J
Johannes Rieken 已提交
132
	updateItems(request: IUpdateRequest): Promise<void> {
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
		let updateCount = 0;
		const serializableRequest: ISerializableUpdateRequest = Object.create(null);

		if (request.insert) {
			serializableRequest.insert = mapToSerializable(request.insert);
			updateCount += request.insert.size;
		}

		if (request.delete) {
			serializableRequest.delete = values(request.delete);
			updateCount += request.delete.size;
		}

		if (updateCount === 0) {
			return Promise.resolve(); // prevent work if not needed
		}

		return this.channel.call('updateItems', serializableRequest);
B
wip  
Benjamin Pasero 已提交
151 152
	}

J
Johannes Rieken 已提交
153
	checkIntegrity(full: boolean): Promise<string> {
B
wip  
Benjamin Pasero 已提交
154 155 156
		return this.channel.call('checkIntegrity', full);
	}

J
Johannes Rieken 已提交
157
	close(): Promise<void> {
158 159 160 161

		// when we are about to close, we start to ignore main-side changes since we close anyway
		this.onDidChangeItemsOnMainListener = dispose(this.onDidChangeItemsOnMainListener);

B
wip  
Benjamin Pasero 已提交
162 163
		return Promise.resolve(); // global storage is closed on the main side
	}
164 165 166 167 168 169

	dispose(): void {
		super.dispose();

		this.onDidChangeItemsOnMainListener = dispose(this.onDidChangeItemsOnMainListener);
	}
B
wip  
Benjamin Pasero 已提交
170
}